携手创作,共同生长!这是我参加「日新计划 8 月更文应战」的第6天,点击查看活动详情

材料来历:

Java泛型T、E、K、V、N、?和Object差异和意义

深入理解Java泛型

1. 泛型

1. 概述

泛型能够把类型清晰的工作推迟到创建目标或调用办法的时分才去清晰的特别的类型 。

相当于把数据类型作为参数来进行传递。

留意:泛型只能是引证数据类型。

2. 泛型类&泛型接口的差异

  • 泛型类和泛型接口的用都相同,下面咱们以泛型类为例进行讲解。
  • 泛型类便是把泛型界说在类上,用户运用该类的时分,才把类型清晰下来 。

3. 泛型符号符

字母 意义
E Element 调集元素
T Type Java类
K Key 键
V Value 值
N Number 数值类型
? 表示不确认的Java类型

这些符号并不是约束只要对应的类型才干运用,即使你统一运用A-Z英文字母的其间一个,编译器也不会报错。之所以又不同的符号符,这是一种约好。 在开发中许多规矩都是一种约好,它能进步咱们代码的可读性,方便团队见的合作开发

4. 泛型类

泛型类的声明与非泛型类简直相同,唯一的不同在于类名的后边添加了参数声明部分

① 界说泛型类

在类名后加<>,在<>中界说泛型,<>中的内容相当于泛型的姓名,能够随意写。 在泛型类中咱们能够把这个泛型的姓名作为一个数据类型来运用。

public class TestClass<T> {
    //...
}

② 运用泛型类

在泛型类中能够运用在类名后鸿沟说的泛型。

public class TestClass<T> {
    public void test(T t){
    }
}

泛型的确认

①创建目标时确认

在创建泛型类目标的时分确认之前界说的泛型代表什么数据类型。在界说泛型类目标的时分,在类名的后加<>,在其间写一个详细的数据类型。

    public static void main(String[] args) {
        TestClass<String>  t = new TestClass();//指定了该目标的泛型T是String类型
        t.test("三更草堂");//所以test办法的参数类型应该也是String类型
    }

②界说子类时确认

在界说子类的时分能够确认泛型。详细用法如下:

public class SubClass extends TestClass<String> {
    @Override
    public void test(String s) {
    }
}

这样在子类SubClass中泛型就确认为String类型了。

留意:咱们在界说子类时也能够挑选不确认泛型,让其在创建目标的时分在确认。写法如下

public class SubClass<T> extends TestClass<T> {
    @Override
    public void test(T t) {
        super.test(t);
    }
}

5. 泛型办法

你能够写一个泛型办法,该办法在调用时能够接纳不同类型的参数。依据传递给泛型办法的参数类型,编译器适当地处理每一个办法调用。

① 界说泛型办法

在办法返回值类型的前面加<>,在<>中界说泛型,<>中的内容相当于泛型的姓名,能够随意写。 在该泛型办法中咱们能够把这个泛型的姓名作为一个数据类型来运用。


    public static  <T> T test(T t){
        return t;
    }

语法规矩:

  • 一切泛型办法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在办法返回类型之前

比如说这是一个用来打印数组的泛型办法:

private static <E> void printArray(E[] inputArray)
  • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。

比如这个办法

private static <E,T> void printArray(E[] inputArray, T data)
  • 类型参数能被用来声明返回值类型,而且能作为泛型办法得到的实际参数类型的占位符
  • 泛型办法体的声明和其他办法相同。留意类型参数只能代表引证型类型,不能是原始类型(int double char等)

② 运用泛型办法

在泛型办法中能够运用界说的泛型。而且咱们一般是在参数列表中或者是返回值类型上运用到这个泛型。

    public static <T> T test(T t){
        return t;
    }

泛型的确认

调用泛型办法的时分才真正确认之前界说的泛型代表什么数据类型。在调用泛型办法的时分,程序会依据你的调用自动推导泛型的详细类型。

    public static void main(String[] args) {
        Integer test = test(1);
        String s = test("三更草堂");
    }

6. 类型通配符

① ?

咱们一般能够运用?来承接一切的引证类型,转移一个菜鸟上的比如:

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));
   }
}

运行成果:

data :icon
data :18
data :314

② ? extends T

这是泛型上鸿沟: 只要T目标的子类能够被传入

如果是? extends C,那么只要D和E答应被传入,不然会编译报错

Java——泛型运用

③ ? super T

这是泛型下鸿沟: 只要T目标的父类能够被传入

如果是? super D,那么只要C和A答应被传入,不然会编译报错

Java——泛型运用

T 和 ?的差异

  • T一般作为泛型参数
  • ? 是更多是用来一个不确认的引证类型

Java——泛型运用

7. 泛型上限&泛型下限

泛型约束的概念

咱们在运用确认泛型的时分能够运用任意的引证数据类型去确认。但是在某些场景下咱们要求这个泛型有必要是某个类的子类或者是某个类的父类。这种情况下咱们就需要用到泛型上限和泛型上限来约束泛型的规模。

泛型上限

约束泛型有必要是某个类或者是其子类。

格局:

  <? extends 详细的类型>

例如:

public static void test(List<? extends Person> t){
​
}
​
//等价于
public static <T extends Person> void test(List<T> t){
}

这样咱们再调用test办法的时分只能存入泛型为Person或者是Person子类的List调集目标。

(由于不做这样的约束是无法运用person的一些独有的办法的,所以要加这样的约束)

泛型下限

约束泛型有必要是某个类或者是其父类。

格局:

<? super 详细的类型>

例如:

public static void test(List<? super Student> t){
​
}
​
// 等价于public static <T super Student> void test(List<T> t){
}

这样咱们再调用test办法的时分只能存入泛型为Student或者是Student父类的List调集目标。

留意事项

  1. 泛型上限能够在界说泛型类和办法参数上运用

    public class Box<E extends Person> {
        E e;
    }
    
  2. 泛型下限主要在办法参数上运用。

8. 泛型实战

摸鱼三天!我写了一个通用的组建树TreeUtil工具