泛型方法
泛型方法使得该方法能够独立于类而发生变化。以下是一个基本的指导原则:无论何时,只要能你做到,你就应该尽量使用范型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情更加明白。另外,对于一个static的方法而言,无法访问泛型类的类型参数,所以,如果static方法需要使用泛型能力,就必须使其成为泛型方法。
要定义泛型方法,只需将泛型参数列表置于返回值之前。
public class GenericMethods {
public <T> void f(T x) {
System.out.println(x.getClass().getName());
}
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
gm.f("");
gm.f(1);
gm.f(1.0);
gm.f(1.0F);
gm.f('c');
gm.f(gm);
}
}
/* Output:
java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Float
java.lang.Character
GenericMethods
*///:~
泛型的擦除
在泛型代码内部,无法获得任何有关泛型参数类型的信息。
java泛型是使用擦除来实现的,这意味着当你使用泛型的时候,任何具体的类型都会被擦除,你唯一知道的就是你在使用一个对象。因此 List< String > 和 List< Integer > 在运行是实际上是相同的类型。这两种形式上都被擦除成为“原生”的类型,即List.
// 不能编译,因为有了擦除,Java编译器无法将Manipulator()必须能够在obj上调用
//f()这一需求映射到HasF拥有f()这一事实上。
class Manipulator<T> {
private T obj;
public Manipulator(T x) {
obj = x;
}
// Error: cannot find symbol: method f():
// public void manipulate() { obj.f(); }
}
public class Manipulation {
public static void main(String[] args) {
HasF hf = new HasF();
Manipulator<HasF> manipulator = new Manipulator<HasF>(hf);
// manipulator.manipulate();
}
} /// :~
//可以编译过的版本
//重用extends关键字,给定泛型类的边界,告诉编译器只能接受这个边界的类型
class Manipulator2<T extends HasF> {
private T obj;
public Manipulator2(T x) { obj = x; }
public void manipulate() { obj.f(); }
} ///:~
编译器实际上会把类型参数替换为它的擦除,T擦除到了HasF。
当你希望代码能够跨多个类工作时,使用泛型才有帮助。
通配符(向上转型)
为什么要使用向上转型?
class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
public class NonCovariantGenerics {
// Compile Error: incompatible types:
List<Fruit> flist = new ArrayList<Apple>();
}
尽管你在第一次阅读这段代码时会认为:“不能将一个Apple容器赋值给一个Fruit容器”.别忘了,泛型不仅和容器相关正确的说法是:“不能把一个涉及Apple的泛型赋给一个涉及Fruit的泛型”.所以它拒绝向上转型.然而实际上这根本就不是向上转型-Apple的list不是fruit的list.Apple的List将持有Apple和Apple的子类型,而Fruit的List将持有任何类型的Fruit,这也包括Apple,但它不是一个Apple的List,它仍旧是Fruit的List.Apple的List的类型上不等价Fruit的List,即使Apple是Fruit的一种类型.
看一个向上转型的例子:
public class Holder<T> {
private T value;
public Holder() {
}
public Holder(T val) {
value = val;
}
//接受T类型的对象
public void set(T val) {
value = val;
}
public T get() {
return value;
}
public boolean equals(Object obj) {
return value.equals(obj);
}
public static void main(String[] args) {
Holder<Apple> Apple = new Holder<Apple>(new Apple());
Apple d = Apple.get();
Apple.set(d);
// Holder<Fruit> Fruit = Apple; // Cannot upcast
Holder<? extends Fruit> fruit = Apple; // OK
Fruit p = fruit.get();
d = (Apple) fruit.get(); // Returns 'Object'
try {
Orange c = (Orange) fruit.get(); // No warning
} catch (Exception e) {
System.out.println(e);
}
// fruit.set(new Apple()); // Cannot call set()
// fruit.set(new Fruit()); // Cannot call set()
System.out.println(fruit.equals(d)); // OK
}
/*
* Output: (Sample) java.lang.ClassCastException: Apple cannot be cast to Orange
* true
*/// :~
如果我们创建一个Holder< Apple >,不能将其向上转型为 Holder< Fruit >,但是可以将其向上转型为Holder<? extends Fruit>.如果调用get()方法,它只会返回一个Fruit------这就是在给定任何扩展自Fruit对象这一边界,它所能知道的一切了.
逆变(超类型通配符)
声明通配符是由某个特定类的任何基类来界定的,方法是指定<? super MyClass>,或者使用类型参数:<? super T> (注意:我们不能对泛型参数给出一个超类型的边界,也就是不能声明< T super MyClass >).这样可以使得你安全的传递一个类型对象到泛型类型中.
例子:
public class GenericWriting {
//无通配符,确切的参数类型
static <T> void writeExact(List<T> list, T item) {
list.add(item);
}
static List<Apple> apples = new ArrayList<Apple>();
static List<Fruit> fruit = new ArrayList<Fruit>();
static void f1() {
writeExact(apples, new Apple());
//不能将Apple放到 List<Fruit>中
// writeExact(fruit, new Apple()); // Error:
// Incompatible types: found Fruit, required Apple
}
static <T> void writeWithWildcard(List<? super T> list, T item) {
list.add(item);
}
static void f2() {
writeWithWildcard(apples, new Apple());
writeWithWildcard(fruit, new Apple());
}
public static void main(String[] args) {
f1();
f2();
}
}
我们重点关注方法writeWithWildcard,它的参数是List<? super T>,因此这个List将持有从T导出的某种具体类型,这样就可以安全地将一个T类型的对象或者从T导出的任何对象作为参数传递给List的方法.
所以总结一下:
1.在使用了Holder<? extends Fruit>之后,Holder的get方法得知到了边界的限定之后,就可以安全的返回了.而set方法这时的参数是? extends Fruit ,这就意味着它可以是任何事物,而编译器无法验证"任何事物"的类型的安全性.
2.在使用Holder<? super Fruit>之后,Holder的set方法由于我们限定了元素的最小粒度的下限,所以可以存储.但是这个时候get方法就无法正常使用了,如果使用get方法,最终只能获取到最大的基类Object.
无界通配符
在泛型参数中<?>表示可以持有任何类型.
//例子1
//我们发现编译器很少关心使用的是原声类型还是<?> .这种情况下,<?>可以被认为是一种装饰.
public class UnboundedWildcards1 {
static List list1;
//这里使用了无界通配符
static List<?> list2;
static List<? extends Object> list3;
static void assign1(List list) {
list1 = list;
list2 = list;
// list3 = list; // Warning: unchecked conversion
// Found: List, Required: List<? extends Object>
}
static void assign2(List<?> list) {
list1 = list;
list2 = list;
list3 = list;
}
static void assign3(List<? extends Object> list) {
list1 = list;
list2 = list;
list3 = list;
}
//list2 都可以向原生的方法一样使用
public static void main(String[] args) {
assign1(new ArrayList());
assign2(new ArrayList());
// assign3(new ArrayList()); // Warning:
// Unchecked conversion. Found: ArrayList
// Required: List<? extends Object>
assign1(new ArrayList<String>());
assign2(new ArrayList<String>());
assign3(new ArrayList<String>());
// Both forms are acceptable as List<?>:
List<?> wildList = new ArrayList();
wildList = new ArrayList<String>();
assign1(wildList);
assign2(wildList);
assign3(wildList);
}
}
//例子2
public class UnboundedWildcards2 {
static Map map1;
static Map<?,?> map2;
static Map<String,?> map3;
static void assign1(Map map) { map1 = map; }
static void assign2(Map<?,?> map) { map2 = map; }
static void assign3(Map<String,?> map) { map3 = map; }
public static void main(String[] args) {
assign1(new HashMap());
assign2(new HashMap());
// assign3(new HashMap()); // Warning:
// Unchecked conversion. Found: HashMap
// Required: Map<String,?>
assign1(new HashMap<String,Integer>());
assign2(new HashMap<String,Integer>());
assign3(new HashMap<String,Integer>());
}
}
1.例子1:我们发现List<?>实际上就是List< Object > .
同样的原生的List表示持有任何Object类型的原声的List.
List<?>表示具有某种特定类型的非原生List,只是我们不知道那种类型是什么.
2.例子2:当我们处理多个泛型参数的时候,有时允许一个参数可以是任何类型,同时可以为其它参数确定某种特定类型的能力.
//Holder是一个泛型类型
public class Holder<T> {
private T value;
public Holder() {
}
public Holder(T val) {
value = val;
}
//set方法传递Object类型是不安全的
public void set(T val) {
value = val;
}
public T get() {
return value;
}
public boolean equals(Object obj) {
return value.equals(obj);
}
}
public class Wildcards {
// Raw argument:
static void rawArgs(Holder holder, Object arg) {
// holder.set(arg); // Warning:
// Unchecked call to set(T) as a
// member of the raw type Holder
// holder.set(new Wildcards()); // Same warning
// Can't do this; don't have any 'T':
// T t = holder.get();
// OK, but type information has been lost:
Object obj = holder.get();
}
// Similar to rawArgs(), but errors instead of warnings:
static void unboundedArg(Holder<?> holder, Object arg) {
// holder.set(arg); // Error:
// set(capture of ?) in Holder<capture of ?>
// cannot be applied to (Object)
// holder.set(new Wildcards()); // Same error
// Can't do this; don't have any 'T':
// T t = holder.get();
// OK, but type information has been lost:
Object obj = holder.get();
}
//返回泛型参数T
static <T> T exact1(Holder<T> holder) {
T t = holder.get();
return t;
}
static <T> T exact2(Holder<T> holder, T arg) {
holder.set(arg);
T t = holder.get();
return t;
}
//为转型设置了边界 包括持有任何扩展自T的对象的Holder,可以接受get方法.
static <T> T wildSubtype(Holder<? extends T> holder, T arg) {
// holder.set(arg); // Error:
// set(capture of ? extends T) in
// Holder<capture of ? extends T>
// cannot be applied to (T)
T t = holder.get();
return t;
}
//超类型通配符,这个方法与wildSubtype方法正好相反,可以接受set方法.
static <T> void wildSupertype(Holder<? super T> holder, T arg) {
holder.set(arg);
// T t = holder.get(); // Error:
// Incompatible types: found Object, required T
// OK, but type information has been lost:
Object obj = holder.get();
}
/**
* @param args
*/
public static void main(String[] args) {
Holder raw = new Holder<Long>();
// Or:
raw = new Holder();
Holder<Long> qualified = new Holder<Long>();
//无界通配符
Holder<?> unbounded = new Holder<Long>();
//向上转型
Holder<? extends Long> bounded = new Holder<Long>();
Long lng = 1L;
rawArgs(raw, lng);
rawArgs(qualified, lng);
rawArgs(unbounded, lng);
rawArgs(bounded, lng);
unboundedArg(raw, lng);
unboundedArg(qualified, lng);
unboundedArg(unbounded, lng);
unboundedArg(bounded, lng);
// Object r1 = exact1(raw); // Warnings:
// Unchecked conversion from Holder to Holder<T>
// Unchecked method invocation: exact1(Holder<T>)
// is applied to (Holder)
Long r2 = exact1(qualified);
Object r3 = exact1(unbounded); // Must return Object
Long r4 = exact1(bounded);
// Long r5 = exact2(raw, lng); // Warnings:
// Unchecked conversion from Holder to Holder<Long>
// Unchecked method invocation: exact2(Holder<T>,T)
// is applied to (Holder,Long)
Long r6 = exact2(qualified, lng);
// Long r7 = exact2(unbounded, lng); // Error:
// exact2(Holder<T>,T) cannot be applied to
// (Holder<capture of ?>,Long)
// Long r8 = exact2(bounded, lng); // Error:
// exact2(Holder<T>,T) cannot be applied
// to (Holder<capture of ? extends Long>,Long)
// Long r9 = wildSubtype(raw, lng); // Warnings:
// Unchecked conversion from Holder
// to Holder<? extends Long>
// Unchecked method invocation:
// wildSubtype(Holder<? extends T>,T) is
// applied to (Holder,Long)
Long r10 = wildSubtype(qualified, lng);
// OK, but can only return Object:
/*
* compile error xukun Object r11 = wildSubtype(unbounded, lng);
*/
Long r12 = wildSubtype(bounded, lng);
// wildSupertype(raw, lng); // Warnings:
// Unchecked conversion from Holder
// to Holder<? super Long>
// Unchecked method invocation:
// wildSupertype(Holder<? super T>,T)
// is applied to (Holder,Long)
wildSupertype(qualified, lng);
// wildSupertype(unbounded, lng); // Error:
// wildSupertype(Holder<? super T>,T) cannot be
// applied to (Holder<capture of ?>,Long)
// wildSupertype(bounded, lng); // Error:
// wildSupertype(Holder<? super T>,T) cannot be
// applied to (Holder<capture of ? extends Long>,Long)
}
} /// :~
使用确切的类型来替代通配符类型的好处是,可以用泛型参数来做更多的事情,但是使用通配符使得你必须接受范围更宽的参数化类型作为参数.