java 泛型通配符和边界

1. 通配符

泛型中常用的通配符:

我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V ,?等等,这些通配符又都是什么意思呢?

  • ? 表示不确定的 java 类型
  • T (type) 表示具体的一个java类型
  • K V (key value) 分别代表java键值中的Key Value
  • E (element) 代表Element

比如在不确定泛型参数的具体类型时,可以使用?代替,比如下方的例子:
比如public void set(List list)
在调用方法时,参数类型可以是任何参数,既可以是List也可以是List。

2. 边界

A是带通配符的参数化类型,它可以作为普通类型那样用于变量和方法声明等中。

用法例如下方的样式:

1. A a = ...;

2. public void foo(A a) { ... }

3. List l;

表示上边界限定,泛型参数只能是Number及其子类,一旦实例化其他参数类型则会报错。
表示下边界限定,泛型参数只能是Number和它的父类,如果传入的参数类型不是 Number 或者 Number 的子类,无法编译成功。
表示无界通配符,可以表示任意的参数类型。

java 泛型通配符和边界_第1张图片

3. T和?的区别

?和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ? 不行,比如如下这种 :

// 可以操作
T t = operate();

// 不能操作
? car = operate();

T 是一个确定的类型(确定是因为通过 T 来确保泛型参数的一致性这方面来说的),通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。

3.1. 通过 T 来确保多个泛型参数的一致性

// 通过 T 来确保泛型参数类型的一致性
public  void Hello(List dest, List src)

// 通配符类型是不确定的,所以这个方法不能保证两个 List 具有相同的参数类型
public void Hello(List dest, List src)

下面的例子由于泛型指定的数据类型是Number,但是在赋值时给出的数据类型是String,所以 发生数据类型无法相互转换的错误。改正错误的办法是赋值时使用Number的数据类型进行赋值。

import java.util.ArrayList;
import java.util.List;


class Generic {
    public  void genericTest(List dest, List src) {

    }
}


public class GenericLearn {
    public static void main(String[] args) {
        List a = new ArrayList<>();
        List b = new ArrayList<>();
        // List a = new ArrayList();
        // List b = new ArrayList();

        Generic generic = new Generic();
        generic.genericTest(a, b);
    }
}
----------------------------------------------------
Exception in thread "main" java.lang.RuntimeException: 
Uncompilable source code - Erroneous sym type: com.zhbi.source.Generic.genericTest
	at com.zhbi.source.GenericLearn.main(GenericLearn.java:22)
Java Result: 1

下方为改正错误后的代码:

import java.util.ArrayList;
import java.util.List;


class Generic {
    public  void genericTest(List dest, List src) {

    }
}


public class GenericLearn {
    public static void main(String[] args) {
        List a = new ArrayList<>();
        List b = new ArrayList<>();
        // List a = new ArrayList();
        // List b = new ArrayList();

        Generic generic = new Generic();
        generic.genericTest(a, b);
    }
}

3.2. 类型参数可以多重限定而通配符不行

使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 A 和 B 的共有子类型,此时被修饰的变量就具有了所有限定的方法和属性。对于通配符?来说,因为它不是一个确定的类型,所以不能用于多重限定。

class Generic {
    public  void genericTest(List dest, List src) {

    }
}

但是这两种数据类型必须为接口(interface)类型,比如在这里定义了两个接口A和B,同时在类中也实现了这两个接口,具体看下方的代码。

import java.util.ArrayList;
import java.util.List;


interface A {
    public void a();
}


interface B {
    public void b();
}


class Generic implements A, B {
    public  void genericTest(List dest, List src) {

    }

    @Override
    public void a() {

    }

    @Override
    public void b() {

    }
}


public class GenericLearn {
    public static void main(String[] args) {
        List a = new ArrayList<>();
        List b = new ArrayList<>();

        Generic generic = new Generic();
        generic.genericTest(a, b);
    }
}

3.3. 通配符可以使用超类限定而类型参数不行

类型参数 T 只具有 一种 类型限定方式:
T extends A

但是通配符 ? 可以进行 两种限定:
? extends A
? super A

Class在实例化的时候,T 要替换成具体类。Class它是个通配泛型,? 可以代表任何类型,所以主要用于声明时的限制情况。比如,我们可以这样做申明:

// 可以
public Class clazz;
// 不可以,因为 T 需要指定类型
public Class clazzT;

所以当不知道定声明什么类型的 Class 的时候可以定义一 个Class

public class Test3 {
    public Class clazz;
    // 不会报错
    public Class clazzT;
}

那如果也想 public Class clazzT; 这样的话,就必须让当前的类也指定 T :

public class Test3 {
    public Class clazz;
    // 不会报错
    public Class clazzT;
}

你可能感兴趣的:(Java,Blogs,Java,Java,泛型通配符)