Skip to content

Latest commit

 

History

History
142 lines (100 loc) · 3.95 KB

泛型.md

File metadata and controls

142 lines (100 loc) · 3.95 KB

Java 泛型

1. 什么是泛型

泛型,即"参数化类型".参数化类型即是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用时传入具体的类型(类型实参).泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型).也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法.

2. 泛型只在编译期有效

来看一段示例代码:

List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();

Class stringListClass = stringList.getClass();
Class integerListClass = integerList.getClass();

if (stringListClass.equals(integerListClass)) {
    System.out.println("类型相同");
}

//输出
类型相同

通过上面的例子,可以说明在编译之后程序去泛型化.Java中的泛型只在编译阶段有效.在编译过程中,正确校验泛型结果后,会将泛型的相关信息擦除,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法.也就是说,泛型信息不会进入到运行时阶段.

3. 基本使用

泛型有3种使用方式:

  1. 泛型类
  2. 泛型接口
  3. 泛型方法

3.1 泛型类

public class TestOne<T> {

    private T key;

    public TestOne(T key) {
        this.key = key;
    }

    public T getKey() {
        return key;
    }
}

//test code
TestOne<String> test1 = new TestOne<>("aaa");
System.out.println(test1.getKey());

TestOne<Integer> test2 = new TestOne<>(2);
System.out.println(test2.getKey());

//输出
aaa
2

传入泛型参数后,编译器在传入实参的时候会进行类型检查,此时泛型才会发挥它真正的意义.

  1. 泛型的类型参数只能是类类型,不能是基本类型.
  2. 不能对泛型类型使用instanceof操作(eg: num instanceof TestOne<Number>),原因在于泛型擦除

3.2 泛型接口

泛型接口与泛型类的定义及使用是基本一致的,泛型接口常用在各种类的生产器中,如AsyncTask等

//定义一个泛型接口
public interface Generator<T> {
    public T next();
}

未传入泛型实参时,需要将泛型的声明延伸到类上

public class GeneratorImpl<T> implements Generator<T> {
    @Override
    public T show() {
        return null;
    }
}

传入了实参,则不需要在类上增加类型声明了

public class GeneratorImpl implements Generator<String> {
    @Override
    public String show() {
        return null;
    }
}

3.3 泛型方法

泛型方法是指在调用方法的时候指明泛型的具体类型:

public void TestTwo {
    public <T> String show(T t) {
        return t.toString();
    }
}

TestTwo test = new TestTwo();
test.show(312);
test.show("卧槽");

只有声明了<T>的方法才是泛型方法,所以可以在方法中使用泛型类型.

ps: 静态方法无法访问类上定义的泛型,所以只能把泛型定义在方法上.

4. 通配符

private static void show(List<Number> genericity) {
    System.out.println(genericity);
}

//use
List<Integer> numbers = new ArrayList<>();
//show(numbers);  编译报错

上面的代码是会编译报错的,即使Integer是Number的子类. 由此得出同一个泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的.

这时需要使用类型通配符(上下界通配符)来解决.

  • 上界通配符 ? extend Number : 表示传入的类型实参必须是Number或Number的子类
  • 下界通配符 ? super Integer : 表示传入的类型实参必须是Integer或是其父类

ps: 当为泛型添加上下边界时,必须与泛型的声明在一起.private <T extends Integer> T show(List<T> list) { return null; }