Java泛型及类型校验

泛型是什么

  • 泛型(Generics)是编程语言中的一种类型参数化机制,它允许在定义类、接口或方法时使用类型参数 。这些类型参数可以在实际使用时被具体的类型所替换,从而实现代码的复用性和灵活性。通过泛型,可以编写更加通用的组件,同时保持类型安全。

引入

  • 在实际的需求中,我们可能会遇见需要接收一个参数但是参数的类型不能确定,最简单的方法就是定义多个类型成员变量, 但随着接收参数的类型增加,去类中创建多个成员变量就会显得很麻烦,比如:当data变量可能为整型、浮点型、布尔型、字符型、字符串型的任何一种,总不能都去创建一个该类型的data成员变量吧,这时候就需要使用泛型了

泛型的应用

泛型在类中的应用

  • 如下方代码所示,我们使用了<T>给该类指定了一个需要接收的参数,这个参数实际上要接收的是一个类型值。这就是泛型参数,而后面的属性的类型,方法参数的类型就是由参数T来决定的:
java 复制代码
package com.generic;
//public class Box{  
public class Box<T>{

    // private int data;  
    private T data;  
    // private String data;


    //public void setData(int data) {  
    //      this.data = data;        }

    public void setData(T data) {  
            this.data = data;  
    }  

    //public int getData() {  
    //  return data;  
    //}  
    public T getData() {  
        System.out.println("data类型是:" + this.data.getClass());    
        return data;  
    }  
}
  • 我们在GenericMain类中使用Box类,我们先指定Box的类型,在创建Box的对象,如下方代码所示:
java 复制代码
public class GenericMain {  
    public static void main(String[] args){  
        Box <boxInter> = new Box<>();  
        boxInter.setData(9);

        Box<String> boxString = new Box<>();  
        boxString.setData("Hello World");

        //测试用例:
        int data = boxInter.getData();  
        System.out.println("data的值是:" + data); 
        String dataStr = boxString.getData();   
        System.out.println("data的值是" + dataStr); 
        //输出案例:  

        //data类型是:class java.lang.Integer  
        //data的值是:9
        //data类型是:class java.lang.String  
        //data的值是Hello World  
     }
}
  • 拓展:实际上我们平时使用的ArrayList容器就是利用率泛型来成为不同类型的容器,即为先指定类型后创建与类型匹配的对象 ,可以按住ctrl+右击查看ArrayList的源码,会发现实际上ArrayList的源码中也有一个泛型参数<E>
java 复制代码
//ArrayList的源码开头 
public class ArrayList<E> extends AbstractList<E>{  ...  

泛型在接口的应用(多态--类型校验)

  • 可以创建一个接口,该接口用于实现,根据不同的类型,进行不同的输出,如下方代码所示:
    • 创建一个拥有泛型参数的接口,在接口中创建一个抽象方法
    • 抽象方法中指定传入参数的类型由泛型决定
java 复制代码
public interface Printtable<T> {  
    void print(T data);  
}
  • 创建实现接口的类

    • 先创建一个指定类型为Integer的类
    java 复制代码
    package com.generic;  
    //定义类来实现接口  
    //public class IntegerPrinttable implements Printtable{
    //我们还需要指定处理泛型中的哪种类型,这样写的好处是java会帮我们做类型检查,
    //使用接口的对象的泛型该实现类的泛型是否匹配  
    public class IntegerPrinttable implements Printtable<Integer>{  
    
        // @Override  
        // public void print(Object data) { }
    
        @Override  
        public void print(Integer data) {  
            System.out.println("我被用于指定输出Interger类型的参数" + data);  
            // 提供接口中的抽象方法的具体实现  
        }  
    }  
    • 创建一个类来实现接口,指定类型为String
    java 复制代码
    package com.generic;
    //定义类来实现接口  
    public class StringPrinttable implements Printtable<String>{  
        // 提供接口中的抽象方法的具体实现  
        @Override  
        public void print(String data) {  
            System.out.println("我被用于指定输出String类型的参数" + data);
        }  
    }
  • 在Box类中使用接口

    • 为了使用接口,可以先创建一个接口的引用private Printtable<T> printtable;
    • 创建一个使用该接口的方法printData(直接调用接口的引用拿到的对象中的print方法);
    • 最后我们写一个Box的构造方法,形式参数为Printtable<T> printtable,即为接口的引用,但需注意的是,这里的目的不是传入接口对象(接口本身也不能实例化为对象),而是利用向上转型(父类持有子类对象)持有实现类的对象,利用向上转型的特性,实现多态
    java 复制代码
    public class Box<T>{  
        private T data;  
        private Printtable<T> printtable;  
        //在类中将接口声明,可以看为,我们需要在这个类中使用该接口的实现
        public Box(Printtable<T> printtable) {  
            this.printtable = printtable; 
            //这里传入实现了printtable接口类的对象,这里实际上是向上转型  
            //这里的目的实际上就是在使用print方法时通过接口选择那种print方法  
        }      
        public void setData(T data) {  
            this.data = data;  
        }  
    
        public T getData() {  
            System.out.println("data类型是:" + this.data.getClass());  
            System.out.println(this.data);  
            return data;  
        }  
    
        public void  printData() {  
            printtable.print(this.data);
            //这里我们使用实现了接口的类的对象中的方法  
        }
    }
  • 在主类GenericMain中进行测试

    • 在Box类中我们写了构造方法要求像传入一个接口实现类的对象,至于具体传入哪个则是根据Box对象选择了泛型的哪一种来决定的
    • 之后就可以调用box中的方法了
    • 我们分别调用boxInter.printData(); boxString.printData();,printData会根据父类接口持有的类对象,匹配实现接口的子类的对象中的方法
    java 复制代码
    public class GenericMain {  
        public static void main(String[] args){  
        //创建容器容器中添加元素  
        Box<Integer> boxInter = new Box<>(new IntegerPrinttable());  
        boxInter.setData(9);
    
        //创建容器容器中添加元素
        Box<String> boxString = new Box<>(new StringPrinttable());  
        boxString.setData("Hello World");  
    
        //测试用例:  
        boxInter.printData();  
        boxString.printData();  
        //输出案例:  
        /*   
        我被用于指定输出Interger类型的参数9
            我被用于指定输出String类型的参数Hello World
        */    
        }
    }
  • 泛型在接口中的应用的例子中,即体现了面向对象的多态特性,又体现了泛型能够进行类型校验的功能,在创建box对象的时候,使用的先指定类型,后创建与指定类型匹配的对象

    • 在上面的例子中,我们可以试着创建不匹配指定泛型的接口实现类对象,可以发现因为类型不匹配ide给我们标红报错了
    • 因为我们本应给Printtable<String> printtable赋值public class StringPrinttable implements Printtable<String>,即为父类接口未持有匹配的子实现类

泛型在方法中的应用

  • 创建一个方法,如下方代码所示
java 复制代码
public static<T> void printList(ArrayList<T> list){  
    for(T data:list){  
        System.out.println(data);  
    }  
}
  • 调用方法,如下方代码所示
    • 在这里不需要给public static<T> void printList(ArrayList<T> list)传单独的类型给泛型,因为在printList方法的原型中只要求传入ArrayList的对象,而对象中就已经指定了泛型的类型了
java 复制代码
public static void main(String[] args){  
        //列表泛型方法测试用例是  
        ArrayList<Integer> list = new ArrayList<>();  
        list.add(9);  
        list.add(8);  
        list.add(7);  
        printList(list);
        //这里String会被当作泛型T的值直接传入
}
相关推荐
用户298698530142 小时前
Java: 从 Word 文档中提取文本和图像
java·后端
Soofjan2 小时前
GMP 是怎么来的
后端
悟空码字2 小时前
【保姆级】实现APP分享至微信,看完就能落地
java·后端·微信
毕设源码-郭学长2 小时前
【开题答辩全过程】以 基于Spring Boot“活力青春”健身房管理系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
陈随易2 小时前
我也曾离猝死很近
前端·后端·程序员
毕设源码-钟学长2 小时前
【开题答辩全过程】以 基于SpringBoot的校园快递APP系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
喵个咪2 小时前
GoWind Content Hub|风行,开箱即用的企业级前后端一体内容中台
前端·后端·cms
小码哥_常2 小时前
警惕!别让@Async成为服务器的“资源杀手”
后端
qq_256247052 小时前
解剖大型语言模型:剥开人工智能的“黑盒”
后端