《编程思想》11.泛型

1 . 泛型的主要目的之一就是用来指定容器要持有什么类型的对象。

2 . 简单泛型
(1)有许多原因促成了泛型的出现,而最引人注目的一个原因就是为了创造容器类。

public class Holder3<T> {
    private T a ;
    public Holder3(T a){
        this.a = a ;
    }
    public void set(T a) {
        this.a = a ;
    }
    public T get(){
        return  a;
    }
    
    public static void main(String[] args) {
       Holder3<Automobile>(new Automobile());
        Automobile a = h3.get() ;
    })
}

3 .元组:
(1) 仅一次方法调用就能返回多个对象
(2) 它是将一组对象直接打包存储于其中的一个单一对象。这个容器对象允许读取其中元素,但是不允许向其中存放新的对象。

public class TwoTuple<A ,B> {
    //二维元组 ,public外部可以随意访问,final无法改变数据
    //可以随便使用对象,但无法修改数据
    //声明为final的元素便不能被再赋予其他值了
    public final A first ;
    
    public final B second ;
    
    public TwoTuple(A a,B b){
        first = a;
        second = b;
    }
    
    public String toString(){
        return "("+first+"."+second+")";
    }
}

4 . 泛型方法
(1)同样可以在类中包含参数化方法,而这个方法所在的类可以是泛型类,也可以不是泛型类。是否拥有泛型方法,与其所在的类是否是泛型没有关系。泛型方法使得该方法能够独立于类而产生变化。
(2)如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情变得更加清楚明白。
(3)对于一个static的方法而言,无法访问泛型类的类型参数,所以如果static方法需要使用泛型能力,就必须使其成为泛型方法

public class GenericMethods {
    //要定义泛型方法,只需将泛型参数列表置于返回值之前
    public <T> void f(T x) {
        System.out.print(x.getClass().getName());
    }
    
    public static void main(String[] args) {
        GenericMethods gm = new GenericMethods();
        gm.f("");
        gm.f(1);
    }
}

5 .杠杆利用类型参数推断

//问题:重复自己做过的事情
Map<Person,List<? extends Pet>> petPeople = new HashMap<Person,List<? extends Pet>>();
//工具类:类型参数推断避免了重复的泛型参数列表 ,但代码可读性差
public class New {
  public static <K,V> Map<K,V> map(){
	  return new HashMap<K,V>();
  }
  
  public static <T> List<T> list(){
	  return new ArrayList<T>();
  }
  
  public static <T> LinkedList<T> lList(){
	  return new LinkedList<T>();
  }
  
  public static <T> Set<T> set(){
	  return new HashSet<T>();
  }
  
  public static <T> Queue<T> queue(){
	  return new LinkedList<T>();
  }
  
  public static void main(String[] args){
	  Map<String,List<String>> sls = New.map();
	  List<String> ls = New.list();
	  LinkedList<String> lls = New.lList();
	  Set<String> ss = New.set();
	  Queue<String> qs = New.queue();
  }
}

(1)类型推断只对赋值操作有效,其他时候不起作用。如果你将一个泛型方法调用(例如New.map())作为参数,传递给另一个方法,这时编译器并不会执行类型推断。例如:

public class TestOne { 
    static void f(Map<Person,List<? extends Pet>> petPeople) {};
	public static void main(String[] args) { 
		//f(New.map());  //无效
	} 
}

6.可变参数与泛型方法

public class TestOne {  
	public static <T> List<T> makeList(T... args){
		List<T> result = new ArrayList<T>();
		for(T item : args){
			result.add(item);
		}
		return result;
		
	}
	public static void main(String[] args) { 
		List<String> ls = makeList("A","B","C");
		System.out.println(ls);
	} 
}

7.构建复杂模型
(1)泛型的一个重要好处是能够简单而安全地创建复杂的模型

8.类型擦除
(1)在泛型代码内部,无法获得任何有关泛型参数类型的信息。任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。
(2)例如:List 这样的类型注解将被擦除为List。而普通的类型变量在未指定边界的情况下将被擦除为Object。
(3)边界处的动作:正是因为有了擦除,泛型可以表示没有任何意义的

public class ArrayMaker<T> { 
    //kind被存储为Class<T>,擦除意味着它实际将被存储为Class,没有任何参数。
    //因此,当在使用它时,例如创建数组时,Array.newInstance()实际上并未拥有kind
    //所蕴含的类型信息,因此这不会产生具体的结果,所以必须转型 
	private Class<T> kind ;
	
	public ArrayMaker(Class<T> kind){
		this.kind = kind ;
	}
	
	@SuppressWarnings("unchecked")
	T[] create(int size){
		return (T[])Array.newInstance(kind, size);
	}
	 
	public static void main(String[] args) {  
		ArrayMaker<String> stringMaker = new ArrayMaker<String>(String.class);
		String[] stringArray = stringMaker.create(9);
		System.out.println(Arrays.toString(stringArray));
	} 
}

(4)对于在泛型中创建数组,使用Array.newInstance()是推荐的方式。

9.擦除的补偿
(1)擦除丢失了在泛型代码中执行某些操作的能力。任何在运行时需要知道确切类型信息的操作都将无法工作:

public class Erased<T> {
private final int SIZE = 100 ;

//错误代码
public static void f(Object arg){
	if(arg instanceof T ){};   //Error  因为其类型信息已经被擦除了
	T var = new T();           //Errot
	T[] array = new T[SIZE];   //Errot
	T[] array = (T)new Object[SIZE] ; //Unckecked warning
}
}

有时必须通过引入类型标签来对擦除进行补偿。这意味着你需要显式地传递你的类型的Class对象,以便你可以在类型表达式中使用它。

class Building{};

class House extends Building{};

public class Erased<T> {
	Class<T> kind;
	
	public  Erased(Class<T> kind){
		this.kind = kind ;
	}
	
	public boolean f(Object arg){
		return kind.isInstance(arg);
	}
	
	public static void main(String[] args){
		Erased<Building> ctt1 = new Erased<Building>(Building.class);
		System.out.println(ctt1.f(new Building()));   //true
		System.out.println(ctt1.f(new House()));      //true
	}
}

10.不能创建泛型数组,一般的解决方案是在任何想要创建泛型数组的地方都使用ArrayList

public class ListOfGenerics<T> {
	private List<T> array = new ArrayList<T>();
	
	public void add(T item){
		array.add(item);
	}
	
	public T get(int index){
		return array.get(index);
	}
}

11.边界
(1)Java泛型重用了extends关键字,能够将这个参数限制为某个类型子集

class Colored2<T extends Hascolor> extends HoldItem<T> {}

12.通配符

13.问题
(1).任何基本类型都不能作为类型参数,因此不能创建ArrayList< int >之类的东西,解决之道是使用基本类型的包装类以及Java SE5的自动包装机制。如果创建一个ArrayList< Integer > ,并将基本类型int应用于这个容器。那么你将发现自动包装机制将自动地实现int到Interger的双向转换。

(2)一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两个变体会成为相同的接口。

interface Payable<T> {};

class Employee implements Payable<Employee> {};

class Hourly extends Employee implements Payable<Hourly> {};

Hourly不能编译,因为擦除会将Payable和Payable< Hourly > 简化为相同的类Payable.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值