java泛型语法_Core Java笔记 8.泛型(II) - 高级语法与最佳实践

本章重点:

高级语法:通配符

最佳实践

本文介绍泛型的高级语法已经最佳实践。Java 泛型的很多限制都可以从上篇中的原理(Java 泛型机制) 来解释。

通配符

通配符是使用的角度而言的,在编译器期间执行更加严格的检查。

子类限定通配符

语法:

Pair extends Employee>

继承关系:

63e8ecc6f3a211357c785e9b6fbf4002.png

限制:

只需要从编译器如何翻译代码块的角度思考这些限制。

Employee emp = new Employee();

Manager ceo = new Manager();

Manager cfo = new Manager();

Pair managerBuddies = new Pair(ceo, cfo);

Pair extends Employee> wildcardBuddies = managerBuddies; // OK

// 编译器只知道是某个Employee的子类,编译器推断不出具体的类型

// 限制: 编译器拒绝任何特定类型

// void setFirst(? extends Employee)

wildcardBuddies.setFirst(ceo); // compile-time error

wildcardBuddies.setFirst(emp); // compile-time error

wildcardBuddies.setFirst(new Object()); // compile-time error

// ? extends Employee getFirst()

Employee ret = wildcardBuddies.getFirst(); // OK

超类限定通配符

语法:

Pair super Manager>

继承关系:

9f61151a3a701862c53fe65227c5ceeb.png

限制:

Employee e1 = new Employee();

Employee e2 = new Employee();

Manager ceo = new Manager();

Pair employeeBuddies = new Pair();

Pair super Manager> wildcardBuddies = employeeBuddies; // OK

// 编译器值知道是某个Manager的父类

// 限制: 编译器拒绝任何特定类型

// ? super Manager

Manager ret = wildcardBuddies.getFirst(); // compile-time error

// void setFirst(? super Manager)

wildcardBuddies.setFirst(e1); // compile-time error

wildcardBuddies.setFirst(ceo); // OK

更加严格的写法:

public static T min(T[] a);

// 更加严格的写法

public static > T min(T[] a);

解释:

该泛型方法使用Comparable super T>进行擦除,现在 compareTo 写成:

int compareTo(? super T). 表明只需要 T的父类有 compareTo() 方法即可.

例如:GregorianCalendar 是 Calendar 的子类,Calendar实现了Comparable, 但是 GregorianCalendar 并没有重写Comparable, 所以:

public static T min(T[] a); 是不够的。

补充:无限定通配符

语法:

Pair>

限制:

? getFirst(); // 只能赋给一个Object

void setFirst(?); // 无法调用, Object也不行

存在的理由:

对于简单的操作,代替泛型方法,更加具有可读性。

比如:

public static boolean hasNulls(Pair p)

// 可以写成:

public static boolean hasNull(Pair> p) {

return p.getFirst() == null || p.getSecond() == null;

}

局限:

? 不能作为一种类型。? t = xx 非法。

这样导致只能代替操作简单的泛型方法。比如swap都写不了.

public static void swap(Pair> p) {

? t = p.getFirst; // ERROR

p.setFirst(p.getSecond);

p.setSecond(t);

}

// 这时候只能:

public static void swapHelper(Pair p) {

T t = p.getFirst; // ERROR

p.setFirst(p.getSecond);

p.setSecond(t);

}

public static void swap(Pair> p) {

swapHelper(p);

}

最佳实践

1.不能使用基本类型实例化类型参数. 才有 wrapper 即可.

2.类型查询只适用于原始类型. 即类型检查直接使用原始类型即可.

if (a instanceof Pair) // same as "a instanceof Pair"

if (a instanceof Pair) // T is ignored

Pair p = (Pair)a; // can only test that a is a Pair

Pair stringPair = ...;

Pair employeePair = ...;

if (stringPair.getClass() == employeePair.getClass()) // they are equal, Pair.class

3.泛型类无法扩展 Throwable,也无法抛出泛型类.

// 1. 泛型类无法扩展 Throwable

public class Problem extends Exception {/*...*/} // ERROR -- can't extend Throwable(编译无法通过)

// 2. 不能抛出泛型类示例

public static void doWork(Class t) {

try {

do work

} catch (T e) {// ERROR -- cant't catch type variable

Logger.global.info(...)

}

}

// 正确时间:

public static void doWork(Class t) {

try {

do work

} catch (Throwable realCause) {

t.intCause(realCause);

throw t;

}

}

4.参数化类型的数组不合法,而是采用容器类存储.

Pair[] table = new Pair[10]; // ERROR

5.可以声明类型参数,但不能实例化类型参数

new T(...), new T[...], T.class 非法. 但是可以声明.

问题1.

public Pair() { first = new T(); second = new T(); } // ERROR

first = T.class.newInstance(); // ERROR

// 最佳实践: 指定T的类型

public static Pair makePair(Class clazz) {

try {

return new Pair(clazz.newInstance(), clazz.newInstance());

} catch (Exception e) {

return null;

}

}

Pair stringPair = Pair.makePair(String.class);// OK

问题2.

public static > T[] minmax(T... a) {

// T[] mm = new T[2]; // ERROR, Type parameter 'T' cannot be instantiated directly

Object[] mm = new Object[2];

mm[0] = a[0];

mm[1] = a[1];

return (T[])mm; // T 被擦成Comparable, 运行时会ClassCastException, 而Object不是Comparable

}

public static void main(String[] args) {

String[] ss = minmax("Tom", "Dick", "Harry"); // ClassCastException!!!

}

// Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable

}

// 最佳实践:

public static > T[] minmax(T... a) {

T[] mm = (T[])Array.newInstance(a.getClass().getComponentType(), 2);

...

return mm;

}

问题3.

public class ArrayList {

private T[] elements;

public ArrayList() { elements = (T[]) new Object[10]; }

public T get(int n) { return elements[n]; }

public void set(int n, T e) { elements[n] = e; }

public static void main(String[] args) {

ArrayList src = new ArrayList();

src.set(0, "hello");

src.set(1, "world");

// String[]和Object[]是不同的类型.

// String[] copy = src.toArray(); // Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

String[] copy2 = new String[100];

src.toArray(copy2);

System.out.println(Arrays.toString(copy2)); // OK

}

public T[] toArray() {

// 可以分析:elements.getClass().getComponentType()实质是Object, 所以返回的类型实质是:Object[],

T[] result = (T[]) Array.newInstance(elements.getClass().getComponentType(), elements.length);

return result;

}

public T[] toArray(T[] src) {

for (int i = 0; i < elements.length; ++i) {

src[i] = elements[i];

}

return src;

}

}

6.不能在静态域或者方法中使用类型变量. 因为静态域是类共享的.

7. 当T和S有继承关系时,Pair 和 Pair 没有继承关系.

8. Java SE5.0 增加了对泛型的反射API.

Java SE5.0 增加了 java.lang.reflect.Type 来支持类型的反射信息.

public void > minmax(T... a);

c67bd5bdf8018d5b6f23cc3be6242c74.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值