《java编程思想——第十五章(泛型)》

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

泛型

15.2 简单泛型##

泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来来保证正确性。

// 类型参数T
public class Holder3<T> {
	private T t;
	public Holder3(T a) {
		this.t = a;
	}
	public void set(T a){
		this.t = a;
	}
	public T get() {
		return t;
	}
	public static void main(String[] args) {
		Holder3<Automobile> h2 =  new Holder3<Automobile>(new Automobile());
		Automobile a = (Automobile) h2.get();

	}
}

使用类型参数T可以暂时不指定类型,在创建对象时必须指明类型。

  1. 一个元组类库
    将一组对象打包存储咋一个对象中称为元组。
/**
 * 元组持有数据
 * @author Administrator
 *
 */

public class TwoTuple<A,B> {

	public final A first;
	public final B second;
	public TwoTuple(A a,B b){
		first = a;
		second = b;
	}
	@Override
	public String toString() {
		return "("+first+","+second+")";
	}
}

可以通过元组,返回一组对象。
2. 一个堆栈类

 // 自定义堆栈
public class LinkedStack<T> {

	private static class Node<U> {
		U item;
		Node<U> next;
		Node() {
			item = null;
			next = null;
		}
		Node(U item,Node<U> next){
			this.item = item;
			this.next = next;
		}
		boolean end(){
			return item == null && next == null;
		}
	}
	private Node<T> top = new Node<T>();
	
	public void push(T item){
		top = new Node<T>(item, top);
	}
	public T pop(){
		T result = top.item;
		if(!top.end()){
			top = top.next;
		}
		return result;
	}
	
	public static void main(String[] args) {
		LinkedStack<String> s = new LinkedStack<String>();
		for (String string : "Phasers on stun!".split("")) {
			s.push(string);
		}
		
		Print.print(s.pop());
	}
}

使用了末端哨兵来判断堆栈是否为空。
3. RandomList

15.3 泛型接口##

泛型也可用于接口。

public interface Generator<T> {
	T next();
}

泛型接口应用:
生成器利用工程模式生成对象。
利用适配器模式可以实现迭代功能。

15.4 泛型方法##

使用泛型了时,在创建对象时必须指明类型参数;使用泛型方法时,不必指明参数类型。编译器会找出具体的类型,被称为参数推断

定义泛型方法,只需将泛型参数列表置于返回值之前。

//泛型方法
public <T>  void f(T t) {
	System.out.println(t.getClass().getName());
}
  1. 杠杆利用参数推断
    类型推断只对赋值有效,作为参数传递无效。
  2. 可变参数与泛型方法
//可变参数与泛型方法可以很好的共存
public class GenericVarargs {
	public static <T> List<T> makeList(T... args) {
		List<T> result =  new ArrayList<T>();
		for (T t : args) {
			result.add(t);
		}
		return result;
	}
	public static void main(String[] args) {
		List<String> list = makeList("A");
		System.out.println(list);
		list = makeList("A","B","C");
		System.out.println(list);
	}
}

15.5 擦除的神秘之处##

class Frob{}
class Fnorkle{}
class Quark<Q>{}
class Practice<POSITION,MOMENTUM>{}
public class LostInformation {
	public static void main(String[] args) {
		List<Frob> list =  new ArrayList<Frob>();
		Map<Frob,Fnorkle> map = new HashMap<Frob,Fnorkle>();
		Quark<Fnorkle> quark =  new Quark<Fnorkle>();
		Practice<Long,Double> practice = new Practice<Long,Double>();
	
		System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
		System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
		System.out.println(Arrays.toString(quark.getClass().getTypeParameters()));
		System.out.println(Arrays.toString(practice.getClass().getTypeParameters()));

	}
}
输出:
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]

在泛型代码内部,无法获得任何有关泛型参类型的信息。

擦除的代价是:泛型不能显式地应用运行时类型操作。因为所有参数 类型信息都丢失了。

15.5 擦除的补偿##

泛型擦除无法在运行时获取类型信息,可以通过传入类型解决。

/**
 * 通过传入Class获取类型
 * @author Administrator
 */
class Building{}
class House extends Building{}
public class ClassTypeCapture<T> {
	Class<T> kind;
	public ClassTypeCapture(Class<T> kind) {
		this.kind = kind;
	}
	public boolean f(Object arg) {
		return kind.isInstance(arg);
	}
	public static void main(String[] args) {
		ClassTypeCapture<House> ct2 = 
				new ClassTypeCapture<House>(House.class);
		System.out.println(ct2.f(new Building()));
		System.out.println(ct2.f(new House()));
	}
}

泛型擦除创建对象方式:工厂方式和模板方式。
不能创建泛型数组解决方式:使用ArrayList。

15.6 边界##

边界使得你可以在用于泛型的参数类型上设置条件。

<T extends 接口名或类名>

15.7 通配符##

< ? extends 接口名或类名>
任何从某个类继承的类型都适用。
< ? super 接口名或类名> 超类型通配符
无界通配符:<?>

15.8 问题##

  1. 任何基本数据类型都不能作为类型参数
  2. 实现参数化接口
    一个类不能实现同一个泛型接口的两种变体。
  3. 转型和警告
  4. 重载
    由于泛型擦除的原因,重载方法将产生相同签名。
  5. 基类劫持了接口

15.9 自限定类型##

ClassA extens ClassB<A>

自限定会强制要求将正在定义的类当做参数传递给基类。

15.10 动态类型安全##

1.5以后可以使用Collections工具类中的checkedList(list, type),checkedMap()等方法检查传入容器的类型。

15.11 异常##

类型参数可应用于一个方法的throws子句中。

15.12 混型##

混型:混个多个类的能力,产生一个可以表示混型中所有类型的类。

  1. 与接口混合
    使用代理方式,每个混入类型中都有相应的域,使用时代理调用方法。
  2. 装饰器模式
  3. 与动态代理结合

15.13 潜在类型机制##

class Dog:
	def speak(self):
		print "atf:
	def sit(self):
		print "Sitting"
	def reproduce(self):
		pass
class Robot:
	def speak(self):
		print "atf:
	def sit(self):
		print "Sitting"
	def reproduce(self):
		pass
a = Dog()
b = Robot()
perform(a)
perform(b)

perform(anything) 没有针对任何anything类型,anything包含的接口是潜在的。
Java不支持潜在类型机制。

15.17 对缺乏潜在类型机制的补偿##

  1. 反射
    利用反射可以动态地确定所需要的方法并调用它。
  2. 将方法应用于序列
    反射提供了一些有趣的可能,但它将所有的类型检查都转移到了运行时。
    尝试一些实现编译时期检查。
  3. 当你并未拥有正确接口时
    编译时期可以检查类型,但是类型被限制在继承层次之内。
  4. 用适配器仿真潜在类型
    适配器模式模仿潜在类型机制,并在编译时期检查类型。
/**
 * 适配器模式实现潜在类型机制
 * @author Administrator
 *
 * @param <T>
 */
interface Addable<T> { void add(T t);}
class AddableCollectionAdapter<T> implements Addable<T>{
	private Collection<T> c;
	public AddableCollectionAdapter(Collection<T> c) {
		this.c = c;
	}
	
	@Override
	public void add(T t) {
		c.add(t);
	}
	
}
class Adapter{
	public static<T>  Addable<T> collectionAdapter(Collection<T> c) {
		return new AddableCollectionAdapter(c);
	}
}
class AddableSimpleQueue<T> extends SimpleQueue<T> implements Addable<T>{
	public void add(T t) {
		super.add(t);
	}
}
public class Fill2 {
	public static<T> void fill(Addable<T> addable,
			Class<? extends T> classToken,int size) {
		for (int i = 0; i < size; i++) {
			try {
				addable.add(classToken.newInstance());
			} catch (Exception e) {
				throw new RuntimeException();
			}
		}
	}
	public static<T> void fill(Addable<T> addable,
			Generator< T> generator,int size) {
		for (int i = 0; i < size; i++) {
			try {
				addable.add(generator.next());
			} catch (Exception e) {
				throw new RuntimeException();
			}
		}
	}
	
	public static void main(String[] args) {
		List<Coffee> coffees = new ArrayList<>();
		Fill2.fill(new AddableCollectionAdapter<Coffee>(coffees), Coffee.class, 3);
		Fill2.fill(Adapter.collectionAdapter(coffees), Coffee.class, 3);
		for (Coffee coffee : coffees) {
			System.out.println(coffee);
		}
		System.out.println("--------------------");
		AddableSimpleQueue<Coffee> coffeequeue = new AddableSimpleQueue();
		Fill2.fill(coffeequeue, Mocha.class, 5);
		Fill2.fill(coffeequeue, Latte.class, 6);
		for (Coffee coffee : coffeequeue) {
			System.out.println(coffee);
		}
	}
}

15.18 将函数对象用作策略##

策略设计模式将变化的事物隔离到函数对象中。

15.19 总结##

泛型的作用除了提供类型检查,还可以编写更"泛化"的代码。

猜你喜欢

转载自blog.csdn.net/u011067966/article/details/80406591