容器-泛型
-
泛型的定义:
**泛型“理解”**为数据类型的一个占位符(类似:形式参数),告诉编译器,在调用泛型时必须传入实际类型。这种参数类型可以用在类、接口、方法中、分别被成为泛型类、泛型接口、泛型方法。
-
泛型字符
-
泛型类
-
建立一个泛型类Generic
/泛型类,定义泛型:<T>是用于普通类 public class Generic<T> { private T flag;//如果这里没用到泛型,我们要先要定义一个 Object object = new Student(); // 再用object instanceof Student,判断是否是String、integer这些引用类型 // 如果是,当我们要调用这个属性的时候,就要把他强转成String类型或者是integer类型,,否则就会报类型转换异常 //有了泛型之后,这些就变得简单了,也不要去定义Object,也不用去强转,直接在属性面前加一个T的泛型标记 // 通通帮你搞定,他会再你调用的时候帮你直接转成String这些引用类型。 public void setFlag(T flag){ this.flag=flag; } public T getFlag(){ return this.flag; } }
-
再用一个测试类 Test
//当我们用泛型的时候,才能定义泛型的类型,否则我们是不知道的 public class Test { public static void main(String[] args) { Generic<String> generic=new Generic<>(); //如果这里 Generic<String> 没有给定时String类型,generic.setFlag();的时候是Object generic.setFlag("admin"); String flag=generic.getFlag();//如果这里不是泛型来调用的话,要先把他搞成Object,再 // 用instanceof去判断,是否是String类型,是的话调用要进行强转成String类型, //但是这里是泛型的话,直接就可以调用,因为它帮你自动转换成String类型 System.out.println(flag); Generic<Integer> generic1=new Generic<>(); generic1.setFlag(100); Integer flag1=generic1.getFlag(); System.out.println(flag1); } }
-
-
泛型接口
-
先建立一个Igeneric的泛型接口。
//泛型接口,在接口时使用泛型类型 public interface Igeneric<T> { T getName(T name);//当接口使用泛型类型时,方法的类型也可以使用泛型,方法的参数类型也可以使用泛型。 }
-
再定义一个IgenericImpl的接口实现类
//接口实现类,因为Igeneric是泛型的接口,所以要使用具体的泛型Igeneric<String> public class IgenericImpl implements Igeneric<String> { @Override public String getName(String name) { //为什么方法类型和方法参数都是String类型, // 因为你上面实现的是具体的接口时是String类型 return name; } }
-
再来个测试类Test2
public class Test2 { public static void main(String[] args) { //对于实例化对象的引用,现在有两种方法, //1.接口实现类的类型来定义 IgenericImpl igeneric=new IgenericImpl(); String name = igeneric.getName("oldru");//getName();是自动转成String类型的,因为IgenericImpl类的重写方法都是String类型 System.out.println(name); //2.接口类型来定义 Igeneric<String> igeneric1=new IgenericImpl();//这里要特别注意,因为Igeneric是泛型<T>, // 还没给具体的String类型,你这里就要加<String>具体的类型,否则你的getName();还是Object类型 String name1=igeneric1.getName("bjxt"); System.out.println(name1); } }
-
-
泛型方法:泛型不在是通过定义在泛型类上,在去使用泛型方法,而是直接把泛型定义装在方法上,参数类型可以有多个,用逗号隔开。如:<K,V>.
**这样做的好处是:**调用泛型方法时,不在需要像泛型类上去告诉编译器是什么类型,编译器会自动帮你转换。
-
泛型的静态方法:
-
先建立一个MethodGeneric类的泛型方法,这个是个非静态的方法
public class MethodGeneric { //之前的是先把泛型定义在类上,然后再通过类上的泛型,去用在方法上的。 //现在不是,直接把泛型定义装在方法上 //泛型用在方法上,注意<T>要写在返回值类型的前面 //这是没有返回值的非静态方法 public <T> void setName(T name){ System.out.println(name); } //定义有返回值的非静态方法 public <T> T getname(T name){ return name; } }
-
在定义一个测试类Test3
public class Test3 { public static void main(String[] args) { //测试的是没有返回值的非静态方法 MethodGeneric methodGeneric=new MethodGeneric(); //这里的MethodGeneric,不用再去强调具体是什么类型了,因为你在调用的时候,编译器会自动帮你转换 methodGeneric.setName("oldlu"); methodGeneric.setName(12312313); //测试的是有返回值的非静态方法 MethodGeneric methodGeneric2=new MethodGeneric(); String name=methodGeneric2.getname("bjsxt");//getname();的类型是根据我们这个name属性的String类型推断出来的 Integer name1=methodGeneric2.getname(123);//getname();的类型是根据我们这个name1属性的Integer类型推断出来的 System.out.println(name1); //所以,才能正常打印 System.out.println(name); } }
-
-
泛型的静态方法:
-
先建立一个 MethodGeneric类的泛型方法,这是一个静态的方法。
public class MethodGeneric { //定义的是没有返回值的静态方法 public static <T> void setFlag(T flag){ System.out.println(flag); } //定义的是有返回值的静态方法 public static <T> T getFlag(T flag){ return flag; } }
-
再建立一个测试类Test4
public class Test4 { public static void main(String[] args) { //调用静态方法是不用实例化对象的引用的,直接调用 //静态方法没有返回值的调用 MethodGeneric.setFlag("oldlu"); MethodGeneric.setFlag(123123); //静态方法有返回值的调用 String flag=MethodGeneric.getFlag("bjsxt"); System.out.println(flag); Integer flag1=MethodGeneric.getFlag(123132); System.out.println(flag1); } }
-
-
泛型方法的非静态方法和静态方法的区别:
非静态方法可以通过两种方式获得泛型,第一从泛型类中,根据泛型类提供的泛型给泛型方法使用,第二 就是直接在泛型方法直接定义泛型。
静态方法则只能通过在泛型方法上直接定义泛型,不能直接从泛型类中获得。
-
泛型方法中的参数是可变参数:
-
先建立一个MethodGeneric类,去写一个泛型方法的可变参数。
public class MethodGeneric { //泛型方法的参数是可变参数 public <T> void method(T... args){ for (T t:args){ System.out.println(t); } } }
-
在建立一个Test5
public class Test5 { public static void main(String[] args) { MethodGeneric methodGeneric= new MethodGeneric(); String[] arr=new String[]{ "a","b","c"}; Integer[] arr2=new Integer[]{ 1,2,3}; methodGeneric.method(arr); methodGeneric.method(arr2); } }
-
-
泛型的通配符:“?”表示通配符,用于代替具体的类型。
-
先定义一个泛型类,里面有反省过的get、set的方法。
//泛型类,定义泛型:<T>是用于普通类 public class Generic<T> { private T flag; public void setFlag(T flag){ this.flag=flag; } public T getFlag(){ return this.flag; } }
-
再建立一个ShowMsg类,定义一个专门输出Generic类中flag的值的方法。
//Generic<?> ?就是泛型的通配符 public class ShowMsg { //这个方法的作用:就是专门输出Generic类中flag的值 public void showFlag(Generic<?> generic){ //Generic<?> 这里如果不是?通配符,你就要定义一个具体的泛型,比<String>泛型 //但是你如果是Generic<String>,他调用时就只能限定了它只能是<String>才能调用 //如果是别的,比如<Integer>类型,它就调用不了了 //所以为了解决这个问题,我们就使用的通配符?,这样调用的时候就什么泛型都可以调用了 System.out.println(generic.getFlag()); } }
-
在建立一个测试类Test6,测试什么类型都可以打印,就是因为有?通配符。
public class Test6 { public static void main(String[] args) { ShowMsg showMsg= new ShowMsg(); Generic<Integer> generic=new Generic<>(); //这里的Generic<Integer>本来是要和ShowMsg类的Generic<Integer> 的泛型一样的才能调用 //但是,因为下面还有不同的泛型要调用,就会冲突了,所以把ShowMsg类的Generic<?>搞成统配符,这样就谁都可以调用了 //还有注意一个问题Number是Integer的父类,那你的ShowMsg类的Generic<Number> ,是不是以为Number和Integer泛型都可以调用了, //答:不是,因为这里的继承关系是没法在这里使用的 generic.setFlag(20); showMsg.showFlag(generic); Generic<Number> generic1=new Generic<>(); generic1.setFlag(50); showMsg.showFlag(generic1); Generic<String> generic2=new Generic<>(); generic2.setFlag("oldlu"); showMsg.showFlag(generic2); } }
-
-
泛型的统配符的上限限定:通配符的类型是T类以及T类的子类后者是T接口以及T接口的子接口。
-
先定义一个泛型类,里面有反省过的get、set的方法。
//泛型类,定义泛型:<T>是用于普通类 public class Generic<T> { private T flag; public void setFlag(T flag){ this.flag=flag; } public T getFlag(){ return this.flag; } }
-
再建立一个ShowMsg 类,是泛型方法的泛型通配符被上限限定。
//Generic<?> ?就是泛型的通配符 public class ShowMsg { //这个方法的作用:就是专门输出Generic类中flag的值 public void showFlag(Generic<? extends Number> generic){ //泛型通配符的上限限定,说明了泛型的类型继承了Number,也就是缩小了范围 //他的范围只能是Number类型,或者Number类型的子类Integer System.out.println(generic.getFlag()); } }
-
再建立一个测试类Test6
public class Test6 { public static void main(String[] args) { //由于泛型统配符的方法被上限限定了,范围只能在Numeber类型和Numeber的子类Interger类型,只有才这两个类型才能调用,其他类型则无法调用 ShowMsg showMsg= new ShowMsg(); Generic<Integer> generic=new Generic<>(); generic.setFlag(20); showMsg.showFlag(generic); Generic<Number> generic1=new Generic<>(); generic1.setFlag(50); showMsg.showFlag(generic1); /*Generic<String> generic2=new Generic<>(); generic2.setFlag("oldlu"); showMsg.showFlag(generic2);//这里的泛型统配符的被上限限定了,导致这里的String类型就不能使用*/ } }
-
-
**泛型通配符的下限限定:**通配符的类型是T类以及T类的父类后者是T接口以及T接口的父接口。
注意:该方法适用泛型类。
-
先定义一个泛型类,里面有反省过的get、set的方法。
//泛型类,定义泛型:<T>是用于普通类 public class Generic<T> { private T flag; public void setFlag(T flag){ this.flag=flag; } public T getFlag(){ return this.flag; } }
-
建立一个ShowMsg 类,泛型方法的泛型通配符被下限限定。
//Generic<?> ?就是泛型的通配符 public class ShowMsg { //这个方法的作用:就是专门输出Generic类中flag的值 public void showFlag(Generic<? super Integer> generic){ //Generic<? super Integer> generic 泛型统配符的下限限定 //限定的是当前类型和当前类型的父类才能使用 //这样Integer类型能使用,Integer类型的父类Number也能使用 System.out.println(generic.getFlag()) } }
-
再建立一个测试类Test6
public class Test6 { public static void main(String[] args) { //由于泛型统配符的方法被上限限定了,范围只能在Numeber类型和Numeber的子类Interger类型,只有才这两个类型才能调用,其他类型则无法调用 ShowMsg showMsg= new ShowMsg(); Generic<Integer> generic=new Generic<>(); generic.setFlag(20); showMsg.showFlag(generic); Generic<Number> generic1=new Generic<>(); generic1.setFlag(50); showMsg.showFlag(generic1); } }
-
-
泛型的总结:
泛型主要用于编译阶段,编译后生成的字节码class文件包含泛型中的类型信息。**类型编译参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。**因此,使用泛型时,有如下几种错误:
-
基本类型不能用于类型:
如:Test t; 错的,们可以使用对应的包装类,Test t;
-
不能通过类型参数创建对象:
T elm = new T(); 运行时类型参数T会被替换成Object,无法创建T类型的对象,容易引起误解,所以在java中支持。
-