Java核心技术卷一 6. java泛型程序设计

泛型程序设计

泛型程序设计:编写的代码可以被很多不同类型的对象所重用。

类型参数:使用<String>,后者可以省略,因为可以从变量的类型推断得出。类型参数让程序更具更好的可读性和安全性。

通配符类型:很抽象,让库的构建者编写出尽可能灵活的方法。

定义简单泛型类

泛型类就是具有一个或多个类型变量的类。

//引用类型变量 T ,可以有多个类型变量,public class Pair<T, U>{...}

public class Pair<T> {
    //类定义的类型变量制定方法的返回类型以及域和局部变量的类型
    private T first;
    private T second;

    public Pair(){
        first = null;
        second = null;
    }
    public Pair(T first, T second){
        this.first = first;
        this.second = second;
    }

    public T getFirst(){
        return first;
    }
    public T getSecond(){
        return second;
    }

    public void setFirst(T newValue){
        first = newValue;
    }
    public void setSecond(T newValue){
        second = newValue;
    }
}

在实例化类时,加上具体的类型替换类型变量就可以实例化类型。

Pair<String> //用具体的类型替换类型变量就可以实例化泛型类型

//此时,构造器和方法的类型都会赋予
Pair<String>()
Pair<String>(String, String)
String getFirst()
String getSecond()
void setFirst(String)
void setSecond(String)

多个类型变量只要这样即可:

public class Pair<T, U>{...}

调用时的结构:

Pair pair = new Pair<String>();

泛型可以看作普通类的工厂。

泛型方法

定义带有类型参数的简单方法

class Arraylg{
    public static <T> getMiddle(T... a){
        return a[a.length / 2];
    }
}

调用泛型方法:

String middle = Arraylg.<String>getMiddle("John","Q","Public");

类型变量的限定

有时类或方法需要对变量进行约束实现某种功能,对类型变量 T 设置限定实现:

<T extends BoundingType>

多个限定

<T extends Comparable & Serializable>

此时,就可以让类型 T 限制为实现了这些接口。

泛型代码和虚拟机

虚拟机没有泛型类型对象,所有对象都属于普通类。

类型擦除

泛型类型,自动提供了一个相应的原始类型:删去类型参数后的泛型类型名。擦除类型变量,并替换为限定类型(无限定名的变量用 Object)。

经过编译器之后,泛型在虚拟机不存在。

翻译泛型表达式

Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();

编译器吧这个方法调用翻译为两条虚拟机指令:

  • 对原始方法 Pair.getFirst 的调用。
  • 将返回的 Object 类型强制装换为 Employee 类型。

翻译泛型方法

public static <T extends Comparable> T min(T[] a)

public static Comparable min(Comparable[] a)

类型参数 T 擦除,留下限定类型 Compatable 。

类型擦除与多态发生冲突,要在类中生成一个桥方法:

public void setSecong(Object second) { setSecond((Date) second); }

java 泛型转换的事实:

  • 虚拟机中没有泛型,只有普通的类和方法。
  • 所有的类型参数都用它们的限定类型替换。
  • 桥方法被合成来保持多态。
  • 为保持类型安全性,必要时插入强制类型转换。

约束与局限性

不能用基本类型实例化类型参数

可以用包装器类型,如果包装器类型不能接受替换时,可以使用独立的类和方法处理他们

运行时类型查询只适合于原始类型

无论何时对泛型使用 instanceof 会得到一个编译器错误;

泛型类型的强制转换表达式都会看到一个编译警告。

getClass 方法总是返回原始类型。

Pair<String> stringPair = ...;
Pair<Employee> employeePair = ...;
stringPair.getClass() == employeePair.getClass();

if (a instanceof Pair<String>) //Error
Pair<String> p = (Pair<String>) a; //Warning

结果为 true 都将返回 Pair.class

不能创建参数化类型的数组

Pair<String>[] table = new Pair<String>[10];//error

可以声明通配符类型的数组,然后进行类型转换:

Pair<String>[] table = (Pair<String>[]) new Pair<?>[10];

但是结果不安全

安全方法:

ArrayList<Pair<String>>

Varargs 警告

向参数个数可变的方法传递一个泛型类型的实例:

public static <T> void addAll(Collection<T> coll, T... ts){
    for (t : ts) coll.add(t);
}

此时需要 ts 数组,但是他违反了规则,会得到一个警告,可以通过注释消除警告:

@SafeVarargs
public static <T> void addAll(Collection<T> coll, T... ts)

此时可以提供泛型类型来调用这个方法了。

不能实例化类型变量

非法:public Pair() { first = new T(); second = new T(); } // Error

同样反射调用也非法:fisrt = T.class.newInstance(); // Error

不能构造泛型数组

非法:T[] mm = new T[2];

泛型类的静态上下文中类型变量无效

不能在静态域或方法中引用类型变量。

private static T s; // Error
public static T getS(){...}; // Error

不能抛出或捕获泛型类的实例

非法:public class Problem<T> extends Exception { /*...*/ } // Error

catch 子句中不能使用类型变量。

在异常规范中使用类型变量是允许的:public static <T extends Throwable> void doWork(T t) throws T // OK

可以消除对受检查异常的检查

异常处理规定,必须为所有受查异常提供一个处理器。不过可以利用泛型消除这个限制。

注意擦除后的冲突

当泛型类型被擦除时,无法创建引发冲突的条件。

要想支持擦除的转换,就需要强行限制一个类或类型变量不能同时成为两个接口类型的子类,而这两个接口是同一接口的不同参数化。

非法:

class Employee implements Comparable { ... }

class Manager extends Employee implements Comparable<Manager> { ... } //Error

Manager会实现Comparable<Employee>Comparable<Manager>,这是同一接口的不同参数化。

泛型类型的继承规则

Pair<Manager> 不是 Pair<Employee> 的子类。可以将参数类型转换为一个原始类型,但是会产生类型错误。

无论 S 与 T 有什么关系,Pair<Manager>Pair<Employee>基本没什么关系。

通配符类型

通配符概念

通配符类型中,允许类型参数变化。

Pair<? extends Employee>

便是任意泛型 Pair 类型,它的类型参数是 Employee 子类。

Pair<Manager>Pair<Employee>Pair<? extends Employee>的子类型。

通配符的超类型限定

超类型限定:

? super Manager

这个通配符限制为 Manager 的所有超类型。

public static <T extends Comparable<T>> T min(T[] a)

public static <T extends Comparable<? super T>> T min(T[] a) //用于处理需要泛型类型超类型的情况

无限定通配合

无限定通配符:Pair<?>

这个类有如下方法:

? getFirst()
void setFirst(?)

getFirst 能赋予一个 Object;

setFirst 方法不能被调用,也不能用 Object 调用。但是可以调用setFirst(null)

通配符捕获

通配符不是类型变量,不能在编写代码中使用?作为一种类型。

解决方案:

public static <T> void swapHelper(Pair<T> p){
    T t = p.getFirst();
    p.setFirst(p.getSecond();
    p.setSecond(t);
}

public static void swap(Pair<?> p){
    swapHelper(p);
}

swapHelper 方法的参数 T 捕获通配符。他不知道是哪种类型的通配符,但是这是一个明确的类型。

反射和泛型

泛型 Class 类

Class 类是泛型的,String.class是一个Class<String>类的对象(唯一的对象)。

如果给定的类型确实是 T 的一个子类型,cast 方法就会返回一个现在声明为类型 T 的对象,否则,抛出一个 BadCastException 异常。

如果这个类不是 enum 类或类型 T 的枚举值的数组,getEnumConstants 方法将返回 null。

getConstructorgetdeclaredConstructor 方法返回一个 Constructor<T>对象。Constructor类也已经变成泛型,以便newInstance方法有一个正确的返回类型。

API

T newInstance()
T cast(Object obj)
T[] getEnumConstants()
Class<? super T> getSuperclass()
Constructor<T> getConstructor<Class... parameterTypes)
Constructor<T> getDeclaredConstructor<Class... parameterTypes)

//java.lang.reflect.Constructor<T>
T newInstance(Object... parameters)

使用 Class<T> 参数进行类型匹配

public static <T> Pair<T> makePair(class<T> c) throws InstantiationException, IllegalAccessException {
    return new Pair<>(c.newInstance(), c.newInstance());
}

调用

makePair(Employee.class);

类型参数 T 同 Employee 匹配。

虚拟机中的泛型类型信息

可以使用反射 API 确定:

  • 这个泛型方法有一个叫做 T 的类型参数。
  • 这个类型参数有一个子类限定,其自身又是一个泛型类型。
  • 这个限定类型有一个通配符参数。
  • 这个通配符参数有一个超类限定。
  • 这个泛型方法有一个泛型数组参数。

原文地址:https://www.cnblogs.com/lovezyu/p/9127467.html

时间: 2024-08-15 20:10:05

Java核心技术卷一 6. java泛型程序设计的相关文章

Java核心技术(五) —— 泛型程序设计(2)

本文延续上文,针对泛型程序设计,从以下方面进行讲解: 为何使用泛型程序设计 定义简单的泛型类 泛型方法 类型变量的限定 泛型代码和虚拟机 约束与局限性 泛型类型的继承规则 通配符类型 反射和泛型 4.泛型代码和虚拟机 这里内容比较多且杂,我们先概括几个主要思想: 虚拟机中没有泛型,只有普通的类和方法 所有的类型参数都用他们的限定类型转换 桥方法被合成来保持多态 为保持类型安全性,必要时插入强制类型转换 (1)当定义一个泛型类型时,系统会自动提供一个相应的原始类型.原始类型的名字就是删去类型参数后

Java核心技术(五) —— 泛型程序设计(1)

本文主要针对泛型程序设计,从以下方面进行讲解: 为何使用泛型程序设计 定义简单的泛型类 泛型方法 类型变量的限定 泛型代码和虚拟机 约束与局限性 泛型类型的继承规则 通配符类型 反射和泛型 泛型程序设计,意味着编写的代码可以被很多不同类型的对象所重用. 1.泛型类 一个泛型类就是具有一个或多个类型变量的类,如定义一个Pair类 其中,Pair类引入了一个类型变量T,一般用比较短的大写形式来书写.实际上泛型类可以有多个类型变量,如上述程序中可以定义第一个域和第二个域具有不同的类型 之后,只要用具体

《Java核心技术卷一》之 泛型

一.引言 在学习集合的时候我们会发现一个问题,将一个对象丢到集合中后,集合并不记住对象的类型,统统都当做Object处理,这样我们取出来再使用时就得强制转换类型,导致代码臃肿,而且加入集合时都是以Object,没做类型检查,那么强制转换就容易出错,泛型的诞生就是为解决这些问题. 二.使用泛型 泛型是如何解决这个问题呢?按照上面的问题,我们只需要在创建集合时指定集合元素的类型,那么集合就能记住对象的类型,那当我们加入是就只能按照指定的类型进行加入,而取出使用时也不需要强制转换: ArrayList

Java核心技术卷一 4. java接口、lambda、内部类和代理

接口 接口概念 接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义. 如果类遵从某个特定接口,那么久履行这项服务. public interface Comparable<T>{ int compareTo(T other); } 任何实现 Comparable 接口的类都需要包含 compareTo 方法,并且这个方法的参数必须是一个 T 对象,返回一个整形数值. 接口的特点: 接口中所有方法自动地属于 public,所以接口的方法不需要提供关键字 public .

Java核心技术卷一 5. java异常、断言和日志

处理错误 由于出现错误而使得某些操作没有完成,程序因该: 返回到一种安全状态,并能够让用户执行一些其他命令 允许用户保存所有操作的结果,并以适当的方式终止程序 需要关注的问题: 用户输入错误 设备错误 物理限制 代码错误 当某个方法不能够采用正常的路径完成它的任务,就可以通过另外一个一个路径退出方法.这种情况下,方法并不返回任何值,而是抛出(throw)一个封装了错误信息的对象.要注意这个方法将会立刻退出,并不返回任何值.调用这个方法的代码也将无法继续执行,异常处理机制开始搜索能够处理这种异常状

Java核心技术卷一 8. java并发

什么是线程 每个进程拥有自己的一整套变量,而线程则共享数据. 没有使用多线程的程序,调用 Thread.sleep 不会创建一个新线程,用于暂停当前线程的活动.程序未结束前无法与程序进行交互. 使用线程给其他任务提供机会 将代码放置在一个独立的线程中,事件调度线程会关注事件,并处理用户的动作. 在一个单独的线程中执行一个任务的简单过程: 将任务代码移到实现了 Runnable 接口的类的 run 方法中. public interface Runnable{ void run(); } Runn

java核心技术卷一

java核心技术卷一 java基础类型 整型 数据类型 字节数 取值范围 int 4 +_2^4*8-1 short 2 +_2^2*8-1 long 8 +_2^8*8-1 byte 1 -128-127       浮点类型 数据类型 字节数 取值范围 小数位数 float 4 10^-38~10^38和-10^-38~-10^38 小数位数6-7 double 4 10^-308~10^308和-10^-308~-10^308 15位小数         boolean 类型和char 类

读《java核心技术卷一》有感

过去一个多月了吧.才囫囵吞枣地把这书过了一遍.话说这书也够长的,一共706页.我从来不是个喜欢记录的人,一直以来看什么书都是看完了就扔一边去,可能有时候有那么一点想记录下来的冲动,但算算时间太紧,很多也是有始无终,毕竟在之前研究研究程序也只是自己的一个爱好而已,但没有想到签了一个程序员的工作.唉,这老天也太捉弄人了吧,让一个学电气工程(强电方向)学生毕业之后去写代码,而且是与硬件完全无关的代码.真是白念几年大学了.行了,就行发这么多牢骚吧. <java核心技术>有两个卷,我只看了卷一,从我的感

Java基础语法&lt;十二&gt; 泛型程序设计

1 意义 泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用. 常见应用 : ArrayList 2 K T V E ? object等的含义 类型变量使用大写形式 E – Element (在集合中使用,因为集合中存放的是元素) T – Type(Java 类)(需要时还可以用临近的字母U和S)表示任意类型  S.U.V – 2nd.3rd.4th types K – Key(键) V – Value(值) N – Number(数值类型) ? – 表示不确定的java类型(无限制通配