Java 泛型系列一:泛型类、泛型接口、泛型方法

830 阅读4分钟

本文概述:

  • 本文为 Java泛型系列第一篇文章,以 Java泛型的使用为主题,介绍了泛型类、泛型接口的定义与使用;着重探讨了泛型方法,诸如才什么叫泛型方法、怎么使用泛型方法以及泛型类参数对其中方法的影响等

泛型类与泛型接口的定义

为什么需要泛型:泛型的两大好处

  • 好处一:简化逻辑相同但参数不同造成的代码冗余(避免创建过多的类)

    • 业务需求:实现两数相加
    • 冗余代码示意:
     package generic;
     /*
     * 多种数据类型,执行相同的逻辑*/
     public class NoGeneric {
     ​
         public int addInt(int x,int y){
             return x + y;
         }
     ​
         public Float addFloat(Float a,Float b){
             return a + b;
         }
         
         public static void main(String[] args) {
             NoGeneric ma = new NoGeneric();
     ​
             System.out.println(ma.addInt(1,2));
             System.out.println(ma.addFloat(1.1f,2.2f));
         }
     }
    
    • 泛型优化后的代码:参数类型参数化(泛型 )
  • 好处二:在编译期就检查出错误,避免将其带到运行时

    • 业务需求:向容器中添加数据后,逐个取出
    • 未指定泛型
     package generic;
     ​
     import java.util.ArrayList;
     import java.util.List;
     ​
     /*
     * 泛型好处之二,指定容器中应当存储的数据类型,
     *   让错误在编译期出现,避免将错误带到运行期*/
     public class NoGenericList {
         public static void main(String[] args) {
             List list = new ArrayList();
             list.add("WAsbry");
             list.add("Hello");
             list.add(100);
     ​
             for (int i = 0;i < list.size();i++){
                 String string = (String)list.get(i);
                 System.out.println(string + " ");
             }
         }
     }
    
    • 运行报错:错误的类型转换

    image-20220809172717619

    • 有个细节:在取出数据时,不将其强转成String 类型是不会报错的,因为Object 类型实现了toString 方法

      • 修改数据取出代码
       Object string = list.get(i);
      
      • 运行结果

      image-20220809172938029

怎么自定义泛型

  • 泛型的种类:泛型类、泛型方法、泛型接口

  • 泛型类的定义:

    • 编写思路:

      • 在类后面添加< T >即可,跟普通类没有什么区别
    • 编写细节:不一定是T

    • 完整代码:

     package generic;
     /*
     * 自定义泛型类*/
     public class GenericClass<T> {
         private T data;
     ​
         public GenericClass(){
     ​
         }
     ​
         public GenericClass(T data) {
             this.data = data;
         }
     ​
         public T getData() {
             return data;
         }
     ​
         public void setData(T data) {
             this.data = data;
         }
     ​
         public static void main(String[] args) {
             GenericClass<String> ma = new GenericClass<>();
             ma.setData("Hello");
             System.out.println(ma.getData());
         }
     }
    
  • 泛型接口的定义:

    • 泛型接口代码:
     package generic;
     ​
     public interface GenericInterface<T> {
         //泛型接口中的方法
         public T next();
     }
    
    • 泛型接口的实现类:实现类还是一个泛型类
     package generic;
     ​
     public class ImplGenericInterface<T> implements GenericInterface<T> {
         @Override
         public T next() {//此时重写接口方法的返回值为T (接口泛型)
             return null;
         }
     }
    
    • 泛型接口的实现类:实现类中指定了泛型接口的类型
     package generic;
     ​
     public class ImplSuperGenericInterface implements GenericInterface<String> {
         @Override
         public String next() {//此时重写接口方法的返回值为String (实现类指定的类型)
             return null;
         }
     }
    

泛型方法:

泛型方法简单使用:

  • 与泛型类、泛型接口独立,可单独存在;

  • 泛型方法、泛型类、泛型接口的区别

    • 泛型类:在实例化时指定泛型的具体类型
    • 泛型方法:在调用时告诉编译期泛型的具体类型(可以省略,编译器会自动推断)
  • 问题汇总:

    • 泛型方法可以在普通、泛型类中使用;
    • 泛型方法中,泛型参数可以存在多个
  • 泛型方法存在于普通类中

 package generic;
 ​
 public class GenericMethod {
 ​
     public <T> T genericMethod(T...a){
         return a[a.length/2];
     }
 ​
     public void test(int x,int y){
         System.out.println(x + y);
     }
 ​
     public static void main(String[] args) {
         GenericMethod ma = new GenericMethod();
         ma.test(2,3);
         System.out.println(ma.<String>genericMethod(
             "Hello","world","WAsbry"));
         System.out.println(ma.genericMethod(12,34,15));
     }
 }
  • 运行截图:

image-20220809195340845

在泛型方法的使用

  • 总结:

    • 不是声明在泛型类中的方法就叫泛型方法,关键是看这个方法声明上有无< T >
    • 在泛型类中使用泛型方法,需要在方法访问修饰符与返回类型之间,指定泛型类型
  • 代码展示:

     package generic;
     ​
     public class GenericMethod2 {
         //自定义泛型类(内部类)
         public class Generic<T>{
             //类属性
             private T data;
     ​
             //这个不是泛型方法(泛型方法是一定有<T> 的),只是普通的类的get 方法而已
             public T getData(){
                 return data;
             }
     ​
             //泛型方法的错误写法(编译出错):在泛型定义时,用的是T (定义域使用是相同的)
             public E setData(E data){
                 this.data = data;
             }
     ​
             //
         }
     ​
         //这也不是泛型方法:是泛型类作为参数的普通方法
         public void show(Generic<String> string){
             System.out.println(string);
             return;
         }
     ​
         //泛型方法的错误写法二:使用未定义的类型
         public <T> T show2(Generic<E> data){
     ​
         }
     ​
     //    泛型方法的错误写法三:首先次方法是归属于普通类而非泛型内部类,即T 对于此方法是未声明的
         public void show3(T data){
     ​
         }
     ​
     }
     ​
    

泛型方法细节:

  • 泛型类参数对其中方法的影响

    • 普通方法:参数类型为泛型类指定类型

    • 泛型方法:指定其他泛型类型

      • 调用时,可以传入其指定类型(跟泛型类指定类型相同)
      • 调用是,亦可以传入泛型类指定类型
    • 泛型方法:参数类型与泛型类指定类型相同

      • 调用时,必须传入其指定类型(跟泛型类指定类型相同)
  • 泛型类中的泛型方法可以声明与泛型类指定泛型不同的泛型类型

     package generic;
     ​
     public class GenericMethod3 {
         static class Fruit{
             @Override
             public String toString() {
                 return "fruit";
             }
         }
     ​
         static class Apple extends Fruit{
             @Override
             public String toString() {
                 return "apple";
             }
         }
     ​
         static class Person{
             @Override
             public String toString() {
                 return "person";
             }
         }
     ​
         static class GenericMethodTest<T>{
             public void show(T t){
                 System.out.println(t.toString());
             }
     ​
             public <E> void show1(E e){
                 System.out.println(toString());
             }
     ​
             public <T> void show2(T t){
                 System.out.println(t.toString());
             }
         }
     ​
         public static void main(String[] args) {
             Apple apple = new Apple();
             Person person = new Person();
     ​
             GenericMethodTest<Fruit> ma = new GenericMethodTest<>();
             ma.show(apple);//可以的
             ma.show(person);//不行,编译报错,泛型类参数类型会影响其中的普通方法
             ma.show2(apple);//可以的,已经指定了泛型参数类型
             ma.show1(person);//可以的,泛型类参数类型不会影响其中的泛型方法(这个是指定了类型的)
             ma.show1(apple);//可以的,参数类型不受限
          }
     ​
     }