本文共 9050 字,大约阅读时间需要 30 分钟。
Java集合的缺点:将一个对象放进一个集合时,集合就会忘记这个对象的数据类型,当取出这个对象时,该对象的类型就会变成Object类型,对对象进行使用时要进行相应的类型转换。因此指定下面两种方法。
创建集合时指定类型参数:public static void main(String[] args) { //创建一个只能保存String类型的集合 Listlist=new ArrayList (); list.add("beautiful"); list.add("quickly"); list.forEach(str->System.out.println(((String)str).length())); }
java 7泛型的菱形语法
public static void main(String[] args) { //Java自动推断出ArrayList的<>里是String类型 Listlist=new ArrayList<>(); list.add("beautiful"); list.add("Strongger"); list.forEach(ele->System.out.println(ele)); //java自动推断出HashMap的<>里是String,List Map > map=new HashMap<>(); }
概念:
所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参在声明变量、创建对象、调用方法时动态指定(即传入实际的类型形参,也可传入类型实参)。public interface List{ //接口中的类型E可以作为类型使用 void add(E x); Iterator iterator(); } public interface Iterator { //在接口里E完全可以作为类型使用 E next(); } public interface Map { //在接口里K,V完全可以作为类型使用 Set keySet(); V put(K key,V value); }
包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参,从而可以动态地生成无数多个逻辑上的子类,但这种子类在物理上并不存在
public class Apple{ //使用T类型形参定义实例变量 private T info; //使用类型形参T定义构造器 public Apple(T info) { this.info=info; } public T getInfo() { return info; } public void setInfo(T info) { this.info = info; } public static void main(String[] args) { //由于传给T形参的是String,所以构造器参数指定是String Apple a1=new Apple<>("苹果"); System.out.println(a1.getInfo()); //传给T形参的类型是Double,所以构造器参数指定是Double Apple a2=new Apple<>(5.66); System.out.println(a2.getInfo()); }}
//使用Apple类时为T形参传入String类型//如果使用T类型参数时,所有使用T类型形参的地方都将会被替换成String类型public class A extends Apple
//使用Apple类时不传入实际的形参public class A extends Apple
public class A2 extends Apple{ public String getInfo() { //super.getInfo()方法返回值是Object类型 //必须使用toString()方法才返回String类型 return super.getInfo().toString(); }}
instanceof运算符不能使用泛型类,因为系统中不会存在正真的泛型类
Listlist1=new ArrayList<>();List list2=new ArrayList<>();//下面将返回True,不管泛型的实际参数是什么,运行时总有相同的classSystem.out.println(list1.getClass()==list2.getClass());
public class R{ //下面语句错误,不能在静态变量声明中使用类型形参 static T info; T age; public void foo(T msg){} //下面语句错误,不能在静态方法声明中使用类型形参 public stativ void bar(T msg){}}
如果Foo是Bar的一个子类型(子类或者接口),而G是具有泛型声明的类或接口,G并不是G的子类型,这一点非常注意!
数组和泛型有所不同,假设Foo是Bar的一个子类型(子类或接口),那么Foo[]依然是Bar[]的子类型。
//public void test(List c)//{ // for(int i=0;ic){ for(int i=0;i
上面程序使用的List
List c=new ArrayList();//下面程序引起编译错误c.add(new Object());
将Canvas类修改成下面的形式,就可以把List对象当成List
//定义一个抽象类Shapepublic abstract class Shape{ public abstract void draw(Canvas c);}//定义Shape的子类Circlepublic class Circle extends Shape{ //实现画图方法 public void draw(Canvas c) { System.out.println("在画布"+c+"上画一个圆") }}//定义Shape的子类Rectanglepublic class Rectangle extends Shape{ //实现画图方法 public void draw(Canvas c) { System.out.println("在画布"+c+"上画一个矩形") }}public class Canvas{ //同时在画布上绘制多个形状,使用被限制的泛型通配符 public void drawAll(List shapes) { for(Shape s : shapes) { s.draw(this); } }}
设定类型形参的上限时,要么传给类型形参的类型是上限类型,要么是上限类型的子类
public class Apple{ T col; public static void main(String[] args) { Apple a1=new Apple<>(); Apple a2=new Apple<>(); //下面代码将会引发编译异常,因为String不是Number的子类型 Apple as=new Apple<>(); }}
程序需要为类型形参设定多个上限(至多有一个父类上限,可以有多个接口上限)
public class Apple{ .......}
public class GenericMethodTest { //声明一个泛型方法,该方法中带一个T类型形参 staticvoid fromArrayToCollection(T[] a,Collection c) { for(T o:a) { c.add(o); } } public static void main(String[] args) { Object[] oa=new Object[100]; Collection
在方法中一个类型形参是另一个类型形参的子类时,可以使用如Collection
public class RightTest{ //声明一个泛型方法,两个形参存在继承关系时,只需要提供一个T形参 staticvoid test(Collection from,Collection to) { for(T ele : from) { to.add(ele); } } public static void main(String[] args) { List
//类型通配符方式public interface Collection{ boolean containsAll(Collection c); boolean addAll(Collection c);}
//泛型方法public interface Collection{ boolean containsAll(Collection c); boolean addAll(Collection c);}
如果某方法中一个形参(a)的类型或返回值的类型依赖于另一个形参(b)的类型,则形参(b)的类型声明不应该使用通配符——因为形参(a)或返回值的类型依赖于形参(b)的类型,如果形参(b)的类型无法确定,程序就无法定义形参(a)的类型,在这种情况下只能考虑使用在方法签名中声明类型形参——泛型方法。
泛型构造器
class Foo{ publicFoo(T t) { System.out.println(t); }}public class GenericConstructor{ public static void main(Stringp[] args) { //泛型方法中的T参数是String new Foo("张三"); //泛型方法中的T参数是Integer new Foo(10); //显示指定泛型构造器的T参数是String类型 //传给构造器的实参也是String类型 new Foo("张三"); //下面语句将出现编译错误 new Foo(10); }}
菱形语法
class Foo{ public Foo(T t) { System.out.println(t); }}public class GenericConstructor{ public static void main(Stringp[] args) { //Foo类声明中的E形参是String类型 //泛型方法中的T参数是Integer类型 Foo f1=new Foo<>(10); //显示指定泛型构造器中声明的T形参是Integer类型 Foo f2=new Foo (5); //如果显示指定泛型构造器中声明的T形参是Integer类型 //就不能使用菱形语法,下面语句错误 //Foo f3=new Foo<>(5); }}
public class MyUtil{ //下面dest集合元素的类型必须与src集合元素的类型相同,或是其父类 public staticT copy(Collection dest,Collection src) { T last=null; for(T ele : src) { last=ele; dest.add(ele); } return last; } public static void main(String[] args) { List ln=new ArrayList<>(); List li=new ArrayList<>(); li.add(5); //此处可以确保最后一个被复制的类型是Integet类型 Integer last=copy(ln,lu); System.out.println(ln); }}
当把一个具有泛型信息的对象赋给一个没有泛型信息量的变量是,所有在尖括号之间的类型信息都将被扔掉。
class Apple{ T size; public Apple() {} public Apple(T size) { this.size=size; } public void setSize(T size) { this.size=size; } public T getSize() { return this.size; }}public class ErasureTest{ public static void main(String[] args) { Apple a=new Apple<>(6); //a的getSize()方法返回Integer对象 Integer as=a.getSize(); //把a对象赋给Apple对象,丢失尖括号里的类型信息 Apple b=a; //b只知道size类型是Number Number size1=b.getSize(); //下面语句将发生编译错误 //Integet size2=b.getSize(); }}
擦除
public class ErasureTest2{ public static void main(String[] args) { Listli=new ArrayList<>(); li.add(5); li.add(6); List list=li; //下面代码引起警告,编译、运行时完全正常 List ls=list; //但是要访问集合里面的元素就会发生运行时异常 System.out.println(ls.get(0)); }}
上面的类转换成下面的类:
public class ErasureTest2{ public static void main(String[] args) { Listli=new ArrayList<>(); li.add(5); li.add(6); System.out.println((String)li.get(0)); }}
Java允许创建无上限的通配符泛型数组:
List [] lsa=new ArrayList [10];Object[] oa=lsa;Listli=new ArrayList ();li.add(new Integer(3));oa[1]=li;//下面代码将引发异常String s=(String)lsa[1].get(0);
上面代码修改成如下代码
List [] lsa=new ArrayList [10];Object[] oa=lsa;Listli=new ArrayList ();li.add(new Integer(3));oa[1]=li;Object s=lsa[1].get(0);if(s instanceof String){ //下面代码安全 String s=(String)s;}
转载地址:http://ufkni.baihongyu.com/