java 核心技术卷I学习记录(八)- 泛型程序设计

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhao__zhen/article/details/84428164

java 核心技术卷第八章:泛型程序设计

##1. 为什么要使用泛型程序设计
泛型程序设计(Generic programming) 意味着编写的代码可以被很多不同类型的对象所重用。

2. 定义简单泛型类

public class Pair<T>
{
    private T first;
    private T second;
    public Pair() { first = null ; second = null ; }
    public PairfT first , T second) { this,first = first; this.second = second; }
    public T getFirstO { return first; }
    public T getSecondO { return second; }
    public void setFirst (T newValue) { first = newValue; }
    public void setSecond(T newValue) { second = newValue; }
}

3. 泛型方法

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

String middle = ArrayAlg.<String>getMiddle("]ohnM, "Q.n, "Public");

4. 类型变量的限定

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

下面的记法<T extends BoundingType〉表示T 应该是绑定类型的子类型(subtype)。T 和绑定类型可以是类, 也可以是接口。选择关键字extends 的原因是更接近子类的概念, 并且Java 的设计者也不打算在语言中再添加一个新的关键字(如sub)。一个类型变量或通配符可以有多个限定,如:T extends Comparable & Serializable

5. 泛型代码和虚拟机

无论何时定义一个泛型类型, 都自动提供了一个相应的原始类型( raw type )。原始类型的名字就是删去类型参数后的泛型类型名。擦除( erased) 类型变M, 并替换为限定类型(无限定的变量用Object)。如Pair 原始类型,因为T 是一个无限定的变量, 所以直接用Object 替换。

public class Pair
{
private Object first;
private Object second;
public Pair(Object first, Object second)
{
	this,first = first;
	this.second = second;
}
public Object getFirstO { return first; }
public Object getSecondO { return second; }
public void setFirst(Object newValue) { first = newValue; }
public void setSecond(Object newValue) { second = newValue; }
}

原始类型用第一个限定的类型变量来替换, 如果没有给定限定就用Object 替换。

  • 当程序调用泛型方法时, 如果擦除返回类型, 编译器插入强制类型转换。
Pair<Employee> buddies = . .
Employee buddy = buddies.getFirst();
擦除getFirst 的返回类型后将返回Object 类型。编译器自动插人Employee 的强制类型转换。

需要记住有关Java 泛型转换的事实:

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

6. 约束与局限性

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

不能用类型参数代替基本类型。因此, 没有Pair, 只有Pair。当然,其原因是类型擦除。擦除之后, Pair 类含有Object 类型的域, 而Object 不能存储double 值。

  1. 运行时类型查询 只适用于原始类型
if (a instanceof Pair<String>) // Error
if (a instanceof Pair<T>) // Error
试图查询一个对象是否属于某个泛型类型时, 倘若使用instanceof 会得到一个编译器错误, 如果使用强制类型转换会得到一个警告。

Pair <String> stringPair = . .
Pair < Employee〉employeePair = . .
if (stringPair.getClassO == employeePair .getClassO) // they are equal 这是因为两次调用getClass 都将返回Pair.class。
  1. 不能创建参数化类型的数组
Pair<String>[] table = new Pair<String>[10] ; // Error

可以通过类型ArrayList来克服这一个困难

  1. Varargs 警告

    向参数个数可变的方法传递一个泛型类型的实例。考虑下面这个简单的方法, 它的参数个数是可变的:

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

为了调用这个方法,Java 虚拟机必须建立一个Pair 数组, 这就违反了前面的规则。不过,对于这种情况, 规则有所放松, 你只会得到一个警告,而不是错误。可以采用两种方法来抑制这个警告。一种方法是为包含addAll 调用的方法增加注解@SuppressWamings(“unchecked”)。 或者在Java SE 7 中, 还可以用SafeVarargs 直接标注addAll 方法:

@SafeVarargs
public static <T> void addAll(Collection<T> coll, T... ts)
  1. 不能实例化类型变量

不能使用像new T(…),newT[…] 或T.class 这样的表达式中的类型变量

  1. 不能构造泛型数组

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

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

  4. 注意擦除后的冲突

catch (T e) // Error can't catch type variable

7. 泛型类型的继承规则

泛型类可以扩展或实现其他的泛型类。

8. 通配符类型

  • 通配符类型中, 允许类型参数变化。例如, 通配符类型
    Pair<? extends Employee>
public static void printBuddies(Pair <Employee> p)
{
    Employee first = p.getFirst();
    Employee second = p.getSecondO;
    Systefn.out.println(first.getName() + " and " + second.getNameQ + " are buddies.");
}

但是不能将Pair传递给这个方法!但是当方法改为public static void printBuddies(Pair<? extends Eiployee> p)但是在使用wi1dcardBuddies.setFirst(1owlyEnployee) ; // compile-time error会出现编译时错误。这样将不可能调用setFirst 方法。编译器只知道需要某个Employee 的子类型, 但不知道具体是什么类型。它拒绝传递任何特定的类型。

  • 通配符的超类型限定

Pair <? super Manager>

只能传递Manager 类型的对象, 或者某个子类型(如Executive) 对象。另外, 如果调用getFirst , 不能保证返回对象的类型。只能把它赋给一个Object。

  • 无限定通配符
Pair<?>
?getFirst()
void setFirst(?)

Pair<?> 和Pair 本质的不同在于: 可以用任意Object 对象调用原始Pair 类的setObject方法。

  • 通配符捕获

编写一个交换成对元素的方法:
public static void swap(Pair<?> p)

通配符不是类型变量, 因此, 不能在编写代码中使用“ ?” 作为一种类型。但是可以通过swaphelper来进行操作。

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) ; }

9. 反射和泛型

Java 泛型的卓越特性之一是在虚拟机中泛型类型的擦除。

可以通过反射的方式来获取泛型中的相关类型等必要信息,具体方法可参考反射的具体api

猜你喜欢

转载自blog.csdn.net/zhao__zhen/article/details/84428164