JAVA编程思想笔记--泛型

来源:互联网 发布:高清矩阵厂家 编辑:程序博客网 时间:2024/06/06 20:50

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.

原创粉丝点击