前言:
https://www.cnblogs.com/LoveBB/p/17277662.html
JDK 1.5开始引入Java泛型(generics)这个特性,该特性提供了编译时类型安全检测机制,允许程序员在编译时检测到非法的类型。停,废话就不多说了,这些术语,自己去百度看看吧,反正不管你看不看得懂,我就要把你教懂,所以,这里不是重点。
泛型有四个作用:类型安全、自动转换、性能提升、可复用性。即在编译的时候检查类型安全,将所有的强制转换都自动和隐式进行,同时提高代码的可复用性。着实是废话,但是不说又不行,生怕你们把路走弯了。
public class Student { }
public class Teacher { }
public class Room { }
这谁不会啊,嘿嘿,别着急,这一般就是我们修炼的名门正派功法,这是很标准的"正派"写法。接下来,使用魔教功法进行魔改。
public class 类名 <泛型类型1,...> { // todo }
简简单单
public class Student<T> { }
我好像在骂人,但是你没哟证据
public class Student<S,B> { }
这里注意,范型里面的 S, B, Q 都是自定义的,一般用大些字母表示,个数不限
public class Student<S, B, Q> { }
嘿嘿,个数不限,那就来个不限的吧
public class Student<S, B, Q, V, N, F, D, A, U, I, O, P, L, H, J, K, G> { }
有人说,字母就26个啊,你写重复的话,那不是一样了嘛。对,你是对的,但是魔教功法就在于,他不讲道理,谁说一定要使用大写字母了,下面来个逼死强迫症写法,什么叫无限火力
public class Student<Sq, VIP, diss, QR, LKl, WOrd, Hao, Da, VPP, Ji, Ni, Tai, Mei, N, WoCao, D, CPDD, U, I, Love, Dog, OPHJKG, JiuK, HangZhou, WO, LAi, CoffEr, VCS> { }
怎么样,好好的一个类,经过魔教功法的改造,是不是看起来不一样了。
虽然我们可以乱写,但是魔教功法还是很讲武德的,一般关于大些字母的定义如下
T:任意类型 type E:集合中元素的类型 element K:key-value形式 key V: key-value形式 value N: Number(数值类型) ?: 表示不确定的java类型
// 改造前 public class Student { } // 改造后 public class Student<T> { }
// 正经写法 Student student = new Student(); // 范型写法 Student<T> student = new Student<>(); // 案例1 Student<String> student = new Student<>(); // 案例2 Student<Integer> student = new Student<>(); // 案例3 Student<Teacher> student = new Student<>(); // 案例4:前面都没意思,来试试套娃吧,哈哈,走火入魔了没有,魔功就是魔功,这里自己动手,这样才清晰 Student<Student<Student<Student<Student<String, Student<String, Long>>, Boolean>, Integer>, String>, Student<String, Integer>> student = new Student<>(); // 案例5:那个最长的,我实在懒得写了,后面全部用String代替了 Student< String, BigDecimal, Integer, Boolean, Long, Double, Room, Teacher, String, String, String, String, String, String, String, String, String > student = new Student<>();
咱就是说,用了魔教功法之后,我们 new 出来的对象,想传什么就传什么,有多少个范型参数,就传多少个对象进去。总之就一句话,很强,运用好了,绝对是秒天秒地秒空气。
看过范型类了,那接下来就是接口。说实在的,接口的使用率比类高很多很多
public interface Student { // todo }
public interface 接口名<T> { // todo }
public interface Student<T> { // todo }
public interface Student<S, B> { // todo }
停,就到这里吧,再写下去就不礼貌了,参数是和类一样的,可以N个,英文字母不限大小。
// 改造前 public interface Student { // todo } // 改造后 public interface Student<T> { // todo }
中所周知,接口只能用实现的方式类实例化,java8之后还能用函数式编程,这次就不展开来说了
// 正经实现 public class GoodStudent implements Student{ } // 范型实现 public class GoodStudent<T> implements Student<T>{ } // 这里不用T可不可以呢,可以的,只要子类和父类的范型一样就行 public class GoodStudent<B> implements Student<B>{ } // 实战1 public class GoodStudent<String> implements Student<String>{ } // 实战2 public class GoodStudent<Integer> implements Student<Integer>{ } // 实战N,别忘记了我们还能套娃,还能无限火力N,这里就不演示了
![image-20230331231845229](/Users/zhangch/Library/Application Support/typora-user-images/image-20230331231845229.png)
那说了那么多,这个范型有什么用呢,答案是:接口、类声明中定义的类型形参则可以在整个接口、类中使用。
可以作为参数类型,入参,返回值使用
public class GoodStudent<T> implements Student<T>{ // 范型作为参数类型 private T personality; // 范型接口1,作为入参 public void evaluation(T t){ } // 范型接口2,作为入参和返回值 public T evaluation2(T t){ return t; } }
// 实例化后很简单的,一看就懂,这就是我们正常的写法 public class GoodStudent<String> implements Student<String>{ // 范型参数 private String personality; public void evaluation(String t){ } public String evaluation2(String t){ return t; } } // 案例2 public class GoodStudent<Integer> implements Student<Integer>{ // 范型参数 private Integer personality; public void evaluation(Integer t){ } public Integer evaluation2(Integer t){ return t; } }
public static void main(String[] args) { // String 类型 GoodStudent<String> goodStudent = new GoodStudent<>(); String personality = ""; goodStudent.evaluation(personality); String result = goodStudent.evaluation2(personality); // Integer 类型 GoodStudent<Integer> goodStudent = new GoodStudent<>(); Integer personality = ""; goodStudent.evaluation(personality); Integer result = goodStudent.evaluation2(personality); // 套娃类型,这里我就套一层,不然容易走火入魔 GoodStudent<GoodStudent<String>> goodStudent = new GoodStudent<>(); GoodStudent<String> personality = new GoodStudent<>(); // 内层 String resultString = personality.evaluation2(""); goodStudent.evaluation(personality); // 外层 GoodStudent<String> result = goodStudent.evaluation2(personality); }
// 无返回值,无入参 public void test(){ } // 无返回值,有入参 public void test(String s){ } // 有返回值,无入参 public String test(){ return ""; } // 有返回值,有入参 public String test(String s){ return s; }
// 范型方法就是加上一个范型声明 public <泛型类型> 返回类型 方法名(泛型类型 变量名) { // todo }
// 无返回值,无入参(无意义) public <T> void test(){ } // 无返回值,有入参(不常用) public <T> void test(T s){ } // 有返回值,无入参(不太常用) public <T> T test(){ retrn null; } // 有返回值,有入参(经常用) public <T> T test(T s){ return s; }
public class GoodStudent implements Student { // 无返回值,无入参 public void test() { } // 无返回值,有入参 public void test2(String s) { } // 有返回值,无入参 public String test3() { return ""; } // 有返回值,有入参 public String test4(String s) { return s; } }
public static void main(String[] args) { GoodStudent goodStudent = new GoodStudent(); goodStudent.test(); String s = ""; goodStudent.test2(s); String s1 = goodStudent.test3(); String s2 = goodStudent.test4(s); }
public class GoodStudent implements Student { // 无返回值,无入参(无意义) public <T> void test(){ } // 无返回值,有入参(不常用) public <T> void test2(T s){ } // 有返回值,无入参(不太常用) public <T> T test3(){ return null; } // 有返回值,有入参(经常用) public <T> T test4(T s){ return s; } }
public static void main(String[] args) { // String 范型 GoodStudent goodStudent = new GoodStudent(); goodStudent.test(); String s = ""; goodStudent.test2(s); String s1 = goodStudent.test3(); String s2 = goodStudent.test4(s); // Integer 范型 GoodStudent goodStudent = new GoodStudent(); goodStudent.test(); Integer s = ""; goodStudent.test2(s); Integer s1 = goodStudent.test3(); Integer s2 = goodStudent.test4(s); // Student<String> 范型 GoodStudent goodStudent = new GoodStudent<>(); goodStudent.test(); Student<String> s = new GoodStudent<>(); goodStudent.test2(s); Student<String> s1 = goodStudent.test3(); Student<String> s2 = goodStudent.test4(s); }
范型方法是传什么参数,就是什么返回值
你以为这就完了?要放大招了。
Java泛型的通配符是用于解决泛型之间引用传递问题的特殊语法, 主要有以下三类:
无边界更多是服务于上届和下届的
// 表示类型参数可以是任何类型 public class B<?> { } // 上界:表示类型参数必须是A或者是A的子类 public class B<T extends A> { } // 下界:表示类型参数必须是A或者是A的超类型 public class B<T supers A> { }
public class GoodStudent implements Student{ // String参数 private String personality; // String 作为入参 public void evaluation(String t){ } // String作为入参和返回值 public String evaluation2(String t){ return t; } }
public class GoodStudent<T> implements Student<T>{ // 范型参数 private T personality; // 范型接口1,作为入参 public void evaluation(T t){ } // 范型接口2,作为入参和返回值 public T evaluation2(T t){ return t; } }
public class GoodStudent<T extends String> implements Student<T> { // 范型参数 private T personality; // 范型接口1,作为入参 public void evaluation(T t){ } // 范型接口2,作为入参和返回值 public T evaluation2(T t){ return t; } }
到这里,我已经走火入魔了,后续的后面在写了,从这里我们也能看出,一个“正派”的类,经过”魔教“功法范型的改造之后,已经具备一定的复杂性了。这就是 魔功-范型 带来的威力。
// 改造前 public class GoodStudent implements Student{ // String参数 private String personality; // String 作为入参 public void evaluation(String t){ } // String作为入参和返回值 public String evaluation2(String t){ return t; } } // 改造后 public class GoodStudent<T extends String> implements Student<T> { // 范型参数 private T personality; // 范型接口1,作为入参 public void evaluation(T t){ } // 范型接口2,作为入参和返回值 public T evaluation2(T t){ return t; } }