4

java魔功心法-范型篇 - 天下没有收费的bug

 1 year ago
source link: https://www.cnblogs.com/LoveBB/p/17277942.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前言:
https://www.cnblogs.com/LoveBB/p/17277662.html

什么是范型

JDK 1.5开始引入Java泛型(generics)这个特性,该特性提供了编译时类型安全检测机制,允许程序员在编译时检测到非法的类型。停,废话就不多说了,这些术语,自己去百度看看吧,反正不管你看不看得懂,我就要把你教懂,所以,这里不是重点。


泛型的作用

泛型有四个作用:类型安全、自动转换、性能提升、可复用性。即在编译的时候检查类型安全,将所有的强制转换都自动和隐式进行,同时提高代码的可复用性。着实是废话,但是不说又不行,生怕你们把路走弯了。


泛型的使用

  • 我们写代码,不也是这几样嘛:类、接口、方法。
  • 来个3简单的类,为了让我们看清楚,类的结构
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;
    }
}


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK