1、泛型简介
1、为什么要有泛型
如果我们需要产生多个对象,每个对象的逻辑完全一样,只是对象内的成员变量的类型不同。那么我们如何去做呢?
class MyClass{ private Object data; public MyClass(Object data) { this.data = data; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
public class Demo2 { public static void main(String[] args) { /*** * 解决类的膨胀,缺点是当我们强制类型转换错误的时候编译正常但是在运行的时候会错误(看注释的那句) * 所以引入了泛型~可以很好解决这俩个问题 */ MyClass myClass1 = new MyClass("张三"); System.out.println((String)myClass1.getData()); MyClass myClass2 = new MyClass(2); System.out.println((Integer)myClass2.getData()); //System.out.println((String)myClass2.getData());//java.lang.ClassCastException } }
//创建一个泛型类 class MyClass <T>{ private T obj; public MyClass(T obj) { this.obj = obj; } public T getData() { return obj; } public void setData(T obj) { this.obj = obj; } }
public class Demo1 { public static void main(String[] args) { MyClass<String> myClass1 = new MyClass<>("张三"); System.out.println(myClass1.getData());//这里不许需要强制类型转换 MyClass<Integer> myClass2 = new MyClass<>(20); System.out.println(myClass2.getData()); } }
2、简介
1、泛型是JDK1.5引入的新特性,也是最重要的一个特性
2、泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的
3、泛型的原理就是类型“参数化”,即把类型看做参数。也就是说把所要操作的数据类型看做参数,就像方法的形式参数是运行时传递的值的占位符一样。
4、简单的来说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息
5、泛型可以提高代码的扩展性和重用性
2、泛型类
1、泛型类实例
//创建一个泛型类 class MyClass <T>{ private T obj; public MyClass(T obj) { this.obj = obj; } public T getData() { return obj; } public void setData(T obj) { this.obj = obj; } }
public class Demo1 { public static void main(String[] args) { MyClass<String> myClass1 = new MyClass<>("张三"); System.out.println(myClass1.getData());//这里不许需要强制类型转换 MyClass<Integer> myClass2 = new MyClass<>(20); System.out.println(myClass2.getData()); } }
2、泛型类的常规用法
1、泛型的类型参数可以是泛型类
//创建一个泛型类 class MyClass <T>{ private T obj; public MyClass(T obj) { this.obj = obj; } public T getData() { return obj; } public void setData(T obj) { this.obj = obj; } } public class Demo1 { public static void main(String[] args) { MyClass<String> myClass1 = new MyClass<>("张三"); System.out.println(myClass1.getData());//这里不许需要强制类型转换 MyClass<Integer> myClass2 = new MyClass<>(20); System.out.println(myClass2.getData()); } }
2、泛型类可以同时设置多个类型参数
class GenClass2<T,V>{ private T t; private V v; public GenClass2(T t,V v) { this.t = t; this.v = v; } public T getT() { return t; } public void setT(T t) { this.t = t; } public V getV() { return v; } public void setV(V v) { this.v = v; } } public class Demo2 { public static void main(String[] args) { GenClass2<String,Integer> gen2 = new GenClass2<String, Integer>("张三", 20); System.out.println(gen2.getT() + " " + gen2.getV()); } }
3、泛型类可以继承泛型类
package com.note.generic3; public class Demo3 { public static void main(String[] args) { SubClass<String,Integer> sub = new SubClass<String,Integer>("张三",10); System.out.println(sub.getT()); } } class SuperClass<T>{ private T t; public SuperClass(T t) { this.t = t; } public T getT() { return t; } public void setT(T t) { this.t = t; } } class SubClass<T,V> extends SuperClass<T>{ private V v; public SubClass(T t,V v) { //因为你的父类已经定义了一个有参的构造函数并且父类中没有默认的无参构造方法,此时编译器不会为你调用默认的构造函数, //当子类继承时,必须在自己的构造函数显式调用父类的构造函数,自己才能确保子类在初始化前父类会被实例化, //如果你父类中有无参的构造函数,子类就不会强制要求调用,即你写的那个就可以通过, //编译器会默认帮你调用父类的构造函数。 super(t); this.v = v; } @Override public T getT() { return super.getT(); } }
4、泛型类可以实现泛型接口
package com.note.generic3; public class Demo4 { public static void main(String[] args) { SubClass1<String,Integer> sub = new SubClass1<String,Integer>("张三",10); System.out.println(sub.getT()); sub.showV(100); } } class SuperClass1<T>{ private T t; public SuperClass1(T t) { this.t = t; } public T getT() { return t; } public void setT(T t) { this.t = t; } } interface IInfo<V>{ public void showV(V vv); } class SubClass1<T,V> extends SuperClass1<T> implements IInfo<V>{ private V v; public SubClass1(T t,V v) { super(t); this.v = v; } @Override public T getT() { return super.getT(); } @Override public void showV(V vv) { System.out.println(v); System.out.println(vv); } }
3、限制泛型可用类型
1、在定义泛型类别时,默认在实例化泛型类的时候可以使用任何类型,但是如果想要限制使用泛型类型时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须继承某个类,或者实现某个接口
2、当没有指定泛型继承的类型或接口时,默认使用extends Object,所以默认情况下任何类型都可以做为参数传入
package com.note.generic4; public class Demo1 { public static void main(String[] args) { GenericClass<Dog> dogClass = new GenericClass<Dog>(); dogClass.setObj(new Dog()); dogClass.getObj().eat(); GenericClass<Cat> catClass = new GenericClass<Cat>(); catClass.setObj(new Cat()); catClass.getObj().eat(); //err 这样是错误的~我们对类型参数做了限制 //GenericClass<String> strClass = new GenericClass<String>(); } } //泛型类参数做了限制,只能接收Animal类型或者Animal类的子类类型 class GenericClass<T extends Animal>{ private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } } abstract class Animal{ public abstract void eat(); } class Dog extends Animal{ @Override public void eat() { System.out.println("啃骨头"); } } class Cat extends Animal{ @Override public void eat() { System.out.println("吃鱼肉"); } }
package com.note.generic4; public class Demo2 { public static void main(String[] args) { GenericClass<Dog> dogClass = new GenericClass<Dog>(); dogClass.setObj(new Dog()); dogClass.getObj().eat(); GenericClass<Cat> catClass = new GenericClass<Cat>(); catClass.setObj(new Cat()); catClass.getObj().eat(); //err 这样是错误的~我们对类型参数做了限制 //GenericClass<String> strClass = new GenericClass<String>(); } } //实现接口也是用extends class GenericClass1<T extends Animal1>{ private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } } interface Animal1{ public abstract void eat(); } class Dog1 implements Animal1{ @Override public void eat() { System.out.println("啃骨头"); } } class Cat1 implements Animal1{ @Override public void eat() { System.out.println("吃鱼肉"); } }
4、类型通配声明
1、为什么要有泛型通配符
同一泛型类,如果实例化给定的实际类型不同,则这些实例的类型是不兼容的,不能相互赋值
Generic<Boolean> f1 = new Generic<Boolean>(); Generic<Integer> f2 = new Generic<Integer>(); f1 = f2;//发生编译错误 Generic<Object> f = f1;//f1和f类型并不兼容,发生编译错误 f = f2;// f2和f类型同样不兼容,也会发生编译错误
泛型类实例之间的不兼容性会带来使用的不便。我们可以使用泛型通配符(?)声明泛型类的变量就可以解决这个问题
2、泛型同配的方式
1、“?”代表任意一个类型
Generic<Boolean> f1 = new Generic<Boolean>(); Generic<?> f =f1;
2、和限制泛型的上限相似,同样可以使用extends关键字限定通配符匹配类型的上限
Generic<Dog> f1 = new Generic<Dog>(); Generic<? extends Animal> f =f1;
3、还可以使用super关键词将通配符匹配类型限定为某个类型及其父类型
Generic<Animal> f1 = new Generic<Animal>(); Generic<? super Dog> f =f1;
package com.note.generic5; import com.note.generic5.Animal; import com.note.generic5.Cat; import com.note.generic5.Dog; import com.note.generic5.GenericClass; public class Demo1 { public static void main(String[] args) { GenericClass<Dog> dogClass = new GenericClass<Dog>(); dogClass.setObj(new Dog()); dogClass.getObj().eat(); GenericClass<Cat> catClass = new GenericClass<Cat>(); catClass.setObj(new Cat()); catClass.getObj().eat(); //无限定通配符的使用 GenericClass<?> genericClass = dogClass; ((Dog)genericClass.getObj()).eat(); genericClass = catClass; ((Cat)genericClass.getObj()).eat(); //上边界限定通配符 GenericClass<? extends Animal> subClass = null; subClass = dogClass; subClass.getObj().eat(); //下边界限定通配符 GenericClass<? super Dog> sClass = null; GenericClass<Animal> animalClass = new GenericClass<Animal>(); sClass = animalClass; } } //实现接口也是用extends class GenericClass<T extends Animal>{ private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } } interface Animal{ public abstract void eat(); } class Dog implements Animal{ @Override public void eat() { System.out.println("啃骨头"); } } class Cat implements Animal{ @Override public void eat() { System.out.println("吃鱼肉"); } }
5、泛型方法
1、不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为
访问修饰符 <泛型列表> 返回类型 方法名(参数列表){ 实现代码 }
2、在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中局部变量类型声明
3、类中其它方法不能使用当前方法声明的泛型
4、什么时候使用泛型方法,而不是泛型类呢?
1、添加类型约束只作用于一个方法的多个参数之间、而不涉及到类中的其他方法时
2、施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数
提示:是否拥有泛型方法,与其所在的类是否是泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前
package com.note.generic6; import com.note.generic6.Animal; import com.note.generic6.Cat; import com.note.generic6.Dog; public class Demo1 { public static void main(String[] args) { GenericClass genericClass = new GenericClass (); genericClass.println("abc"); genericClass.println(12); genericClass.println(true); genericClass.println(12.21); genericClass.println(new Dog()); genericClass.println(new Cat()); GenericClass.print(12); } } class GenericClass { //泛型方法 public <T> void println(T t) { System.out.println(t); } //泛型方法重载 public <T extends Animal> void println(T animal) { animal.eat(); } //静态泛型方法 public static <T> void print(T t) { System.out.println(t); } } interface Animal{ public abstract void eat(); } class Dog implements Animal{ @Override public void eat() { System.out.println("啃骨头"); } } class Cat implements Animal{ @Override public void eat() { System.out.println("吃鱼肉"); } }