Java泛型(一)

泛型方法

泛型方法使得该方法能够独立于类而发生变化。以下是一个基本的指导原则:无论何时,只要能你做到,你就应该尽量使用范型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情更加明白。另外,对于一个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)
	}
} /// :~

使用确切的类型来替代通配符类型的好处是,可以用泛型参数来做更多的事情,但是使用通配符使得你必须接受范围更宽的参数化类型作为参数.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值