Java中的泛型总结

前言

在Java SE 5 中Java中迎来了新的技术泛型,而添加泛型的主要原因是为了满足在1999年制定的最早Java开发规范需求之一(JSR14),专家组们花了大概5年左右的时间来定义规范和测试实现,才有了现在的泛型。在Java中增加泛型之前是用继承来实现的。

为什么要引入泛型?

思考这样一个问题,如果现在有一个挑选最大值方法,可以将传入的数值类型的两个参数中选择出最大的数值返回。我们知道Java中的数值类型包括如基本数据类型int、float以及包装引用类型Integer、Float等,如果我们定义这个方法的参数类型为int,那么想要实现float类型的参数的方法,我们可能就需要对该方法进行重载又或者定义该方法的两个参数都为Object,然后通过强制类型转换等进行数值比较。不管是哪种方法都显得不够“优雅”,而泛型就可以解决此问题。
泛型设计可以让编写的代码被很多不同的类型的对象所有重用,这是泛型解决的主要问题之一。

Java中的泛型记号

泛型需要使用 <> 进行标记,内部标记为字母或者为符号 ?,一般使用大写字母.常见的泛型及其含义如下:
E -Element(在集合中使用,因为集合中存放的是元素)
T -Type (Java类型)
K -key(键)
V -Value(值)
N -Number(数值类型)
? - 通配符,表示不确定的java类型

泛型擦除

在正式使用泛型前,还需要先了解一下泛型擦除。泛型的类型在编译阶段会被编译器进行擦除,java编译器生成的字节码是不包含泛型信息的,如果泛型参数无界,则在编译阶段用Object代替,如果泛型参数有界则替换为其边界或对象。也就是说Java编译器在处理泛型时生成的只包含普通的类、接口以及方法。

有界的参数类型:
上界:类型参数的名称,后面紧跟extends关键字,最后紧跟它的上界。
下界:类型参数的名称,后面紧跟super关键字,最后紧跟它的下界。

泛型方法

泛型方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

下面是定义泛型方法的规则:

所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的 )。
每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。

public class GenericMethodTest
{
    
    
   // 泛型方法 printArray                         
   public static < E > void printArray( E[] inputArray )
   {
    
    
      // 输出数组元素            
         for ( E element : inputArray ){
    
            
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }

    public static void main( String args[] )
    {
    
    
        // 创建不同类型数组: Integer, Double 和 Character
        Integer[] intArray = {
    
     1, 2, 3, 4, 5 };
        Double[] doubleArray = {
    
     1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = {
    
     'H', 'E', 'L', 'L', 'O' };

        System.out.println( "整型数组元素为:" );
        printArray( intArray  ); // 传递一个整型数组

        System.out.println( "\n双精度型数组元素为:" );
        printArray( doubleArray ); // 传递一个双精度型数组

        System.out.println( "\n字符型数组元素为:" );
        printArray( charArray ); // 传递一个字符型数组
    }
}

泛型类

泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。

和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

public class Box<T> {
    
    

  private T t;

  public void add(T t) {
    
    
    this.t = t;
  }

  public T get() {
    
    
    return t;
  }

  public static void main(String[] args) {
    
    
    Box<Integer> integerBox = new Box<Integer>();
    Box<String> stringBox = new Box<String>();

    integerBox.add(new Integer(10));
    stringBox.add(new String("菜鸟教程"));

    System.out.printf("整型值为 :%d\n\n", integerBox.get());
    System.out.printf("字符串为 :%s\n", stringBox.get());
  }
}

类型通配符

1、类型通配符一般是使用 ? 代替具体的类型参数。例如 List<?> 在逻辑上是 List,List 等所有 List<具体类型实参> 的父类。

import java.util.*;

public class GenericTest {
    
    

    public static void main(String[] args) {
    
    
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();

        name.add("icon");
        age.add(18);
        number.add(314);

        getData(name);
        getData(age);
        getData(number);

   }

   public static void getData(List<?> data) {
    
    
      System.out.println("data :" + data.get(0));
   }
}

2、类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。

import java.util.*;

public class GenericTest {
    
    

    public static void main(String[] args) {
    
    
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();

        name.add("icon");
        age.add(18);
        number.add(314);

        //getUperNumber(name);//1
        getUperNumber(age);//2
        getUperNumber(number);//3

   }

   public static void getData(List<?> data) {
    
    
      System.out.println("data :" + data.get(0));
   }

   public static void getUperNumber(List<? extends Number> data) {
    
    
          System.out.println("data :" + data.get(0));
       }
}

泛型与反射

Java中的反射不仅可以查看普通类的信息也可以查看有关泛型的信息,如下为反射包中一些可以解析泛型信息的类和接口。

  • CLass 类,描述具体类型
  • Typevariable 接口,描述类型变量(如 T extends Comparable<? super T>)
  • WildcardType 接口,描述通配符(如? superT)
  • ParameterizedType 接口,描述泛型类或接口类型(如 Comparable<? super T>)
  • GenericArrayType 接口: 描述泛型数组(如 T[ ])

可以写一个工具类解析某个类的泛型信息,程序如下:

package fanxing;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.Objects;
import java.util.Scanner;

public class GenericReflectionTest {
    
    

    public static void main(String[] args) {
    
    
        String name;
        if (args.length > 0){
    
    
            name = args[0];
        }else{
    
    
            try(Scanner in = new Scanner(System.in)){
    
    
                System.out.println("Enter Class name:");
                name = in.next();
            }
        }
        try{
    
    
            Class<?> cl = Class.forName(name);
            printClass(cl);
            for (Method e: cl.getDeclaredMethods()){
    
    
                printMethod(e);
            }
        }catch (ClassNotFoundException e){
    
    
            e.printStackTrace();
        }
    }

    public static void printClass(Class<?> cl){
    
    
        System.out.print(cl);
        printTypes(cl.getTypeParameters(), "<", ", ", ">", true);
        Type sc = cl.getGenericSuperclass();
        if (Objects.nonNull(sc)){
    
    
            System.out.print(" extends ");
            printType(sc, false);
        }
        printTypes(cl.getGenericInterfaces(), "implements ", ", ", "", false);
        System.out.println();
    }

    public static void printMethod(Method m){
    
    
        String name = m.getName();
        System.out.print(Modifier.toString(m.getModifiers()));
        System.out.print(" ");
        printTypes(m.getTypeParameters(), "<", ", ", "", true);

        printType(m.getGenericReturnType(), false);
        System.out.print(" ");
        System.out.print(name);
        System.out.print("(");
        printTypes(m.getGenericParameterTypes(), "", ", ", "", false);
        System.out.println(")");
    }

    public static void printTypes(Type [] types, String pre, String sep, String suf,
        boolean isDefinition){
    
    
        if ("extends".equals(pre) && Arrays.equals(types, new Type [] {
    
    Object.class} )){
    
    
            return;
        }
        if (types.length > 0){
    
    
            System.out.print(pre);
        }
        for (int i = 0; i < types.length; i++){
    
    
            if (i > 0){
    
    
                System.out.print(sep);
            }
            printType(types[i], isDefinition);
        }
        if(types.length > 0){
    
    
            System.out.print(suf);
        }
    }

    public static void printType(Type type, boolean isDefinition){
    
    
        if (type instanceof Class){
    
    
            Class<?> t = (Class<?>) type;
            System.out.print(t.getName());
        }else if(type instanceof TypeVariable){
    
    
            TypeVariable<?> t =(TypeVariable<?>) type;
            System.out.print(t.getName());
            if (isDefinition){
    
    
                printTypes(t.getBounds(), " extends ", "&", "", false);
            }
        }else if (type instanceof WildcardType){
    
    
            WildcardType t = (WildcardType) type;
            System.out.print("?");
            printTypes(t.getUpperBounds(), " extends ", "&", "", false);
            printTypes(t.getUpperBounds(), " super ", "&", "", false);
        }else if (type instanceof ParameterizedType){
    
    
            ParameterizedType t = (ParameterizedType) type;
            Type owner = t.getOwnerType();
            if (Objects.nonNull(owner)){
    
    
                printType(owner, false);
            }
            printType(t.getRawType(), false);
            printTypes(t.getActualTypeArguments(), "<", ", ", ">", false);
        }else if (type instanceof GenericArrayType){
    
    
            GenericArrayType t = (GenericArrayType) type;
            System.out.print("");
            printType(t.getGenericComponentType(), isDefinition);
            System.out.print("[]");
        }
    }

}

添加一个泛型类Pair进行测试,程序如下:

package fanxing;

public class Pair <T>{
    
    

    private T first;

    private T second;

    public Pair(){
    
    
        first = null;
        second = null;
    }

    public Pair(T first, T second){
    
    
        this.first = first;
        this.second = second;
    }

    public T getFirst(){
    
    
        return first;
    }

    public T getSecond(){
    
    
        return second;
    }

    public void setFirst(T first) {
    
    
        this.first = first;
    }

    public void setSecond(T second) {
    
    
        this.second = second;
    }
}

测试一下泛型类Pair,程序运行结果如下:
在这里插入图片描述

本文首发于香菜喵,打开微信随时随地读,扫描文章下方二维码 ↓ ↓ ↓

Guess you like

Origin blog.csdn.net/chen15369337607/article/details/126742249