1. 泛型概念
顾名思义,类型参数化(Generics)
2.未检查的类型转换
给一个原生类型赋值一个泛型类型
Box rawBox = new Box(); // rawBox is a raw type of Box<T> Box<Integer> intBox = rawBox; // warning: unchecked conversion
使用一个原生类型引用调用一个泛型类型引用的泛型方法时
Box<String> stringBox = new Box<>(); Box rawBox = stringBox; rawBox.set(8); // warning: unchecked invocation to set(T)
3.有界泛型类型
上届:
public class NaturalNumber<T extends Integer> { private T n; public NaturalNumber(T n) { this.n = n; } public boolean isEven() { return n.intValue() % 2 == 0; } // ... }
下届:
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) { list.add(i); } }
4.多重泛型类型
class A {} interface B {} interface C {} //这里注意 A&B&C 参数列表中,A类型必须在最前面,因为它是一个类类型 class D<T extends A & B & C> { private D d; public D get() { return d; } } class E extends A implements B, C {} void test() { new D<E>().get(); }
5. 泛型与继承
我们有下面的可编译通过的代码
Object someObject = new Object(); Integer someInteger = new Integer(10); someObject = someInteger; // OK public void someMethod(Number n) { /* ... */ } someMethod(new Integer(10)); // OK someMethod(new Double(10.1)); // OK
但是下面的代码将不能通过编译
public void boxTest(Box<Number> n) { /* ... */ } boxTest(new Box<Integer>()); //Error boxTest(new Box<Float>()); //Error
亦即:
上面的图中可以得到:
Box<Number>和Box<Integer>之间没有什么关系,但是它们却都是Object的子类。
这也是对我们经常书写下面代码时不能通过编译的再现(javac 这样处理的原因请参考下面的类型擦除部分)
class A {} class B extends A{}void test() { ArrayList<A> list = new ArrayList<B>(); // Error }
但是你可以使用下面这样的逻辑,这里体现了泛型中的继承思想:
6. 通配和子类型
这也是一种泛型体现继承的方式:
上届通配符,有时候想定义一个方法,想让这个方法给满足指定泛型类型的所有参数子类调用,这时我们可以尝试类似下面的代码:
public static void process(List<? extends Foo> list) { for (Foo elem : list) { // ... } }
这样,将会有如下的代码通过编译:
interface Foo { } class A implements Foo { } class B implements Foo { } public static void process(List<? extends Foo> list) { for (Foo elem : list) { } } void test() { process(new ArrayList<A>()); // ok process(new ArrayList<B>()); // ok } // 久违的 纳尼 List<? extends Foo> list=new ArrayList<A>();
更玄乎的是:
List<?> 是任何 List<T>的父类型,其中T是一个具体类型。
只是它的使用场景是具体的泛型类型中的成员方法不依赖泛型参数时。
7.通配类型的捕获
import java.util.List; public class WildcardError { void foo(List<?> i) { i.set(0, i.get(0)); //不能确定的类型,所以i.get()得到的元素不能被保存 } }
需要提供给一方法来辅助识别泛型参数,使得通过编译
public class WildcardFixed { void foo(List<?> i) { fooHelper(i); } // Helper method created so that the wildcard can be captured // through type inference. private <T> void fooHelper(List<T> l) { l.set(0, l.get(0)); } }
下面的这段代码是不能通过编译的
List<Integer> list = new ArrayList<Integer>(); List<? extends Number> list2 = list;// ok list2.add(new Integer(1)); // compile-time error
8.类型擦除
下面这段文字是引自java官方文档
Type Erasure Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to: Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods. Insert type casts if necessary to preserve type safety. Generate bridge methods to preserve polymorphism in extended generic types. Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.
泛型中的helper方法保证继承中的多态
public class Node { private Object data; public Node(Object data) { this.data = data; } public void setData(Object data) { System.out.println("Node.setData"); this.data = data; } } public class MyNode extends Node { public MyNode(Integer data) { super(data); } public void setData(Integer data) { System.out.println(Integer data); super.setData(data); } } // MyNode在被javac编译后有如下的内容 class MyNode extends Node { // Bridge method generated by the compiler // public void setData(Object data) { setData((Integer) data); } public void setData(Integer data) { System.out.println("MyNode.setData"); super.setData(data); } // ... }
使用javap对MyNode.class 反编译可以得到具体字节码内容。
时间: 2024-11-06 09:34:18