java 类型变量使用通配符后如何调用?

有一个泛型类 A


class A<T extends Person>
{
    public void test(T val)
    {
        val.say();
    }
}

然后具有父子关系的普通类:

class Person {
    
}

class Male extends Person {
    public void say()
    {
        System.out.println("i am male");
    }
}

使用通配符代码运行以下代码后,无法执行!这是为什么?

class App {
    public static void main(String[] args)
    {
        // male 实例
        Male male = new Male();
        // 泛型类 A<Male>
        A<Male> maleA = new A<>();
        // 泛型类 A<? extends Person>
        A<? extends Person> personA = maleA;
        // 这个地方为什么报错?
        // 我看书中的解释是说 A<? extends Person> 中
        // 由于 ? 是通配符,虽然意思表明需要提供的类型
        // 为 Person 的子类型,但是由于通配符的原因
        // 导致该方法实际上是拒绝任何类型的
        // 所以实际上是成了无法调用的方法
        // 这是为什么?
        personA.test(male);
    }
}

使用了 ? 通配符后,即使提供正确的参数也无法成功调用(从代码上看...),这是为什么?

阅读 2.4k
2 个回答

学java泛型的时候要抛弃面向对象里面继承那一套东西,始终从类型安全角度来考虑。

A<Male> maleA = new A<>();
A<? extends Person> personA = maleA;
personA.test(male);

思考一下,从编译器的角度来考虑,在这段程序里面,personA的具体泛型类型是什么?
答案是不知道,编译器只知道是Person或其子类型,但无法确定具体类型是什么。所以把male传入test方法是不安全的。

举个例子,如果male允许,那么假设我有一个Female类,同样继承Person,那么下面这段代码:

Female female = ……
A<Male> maleA = new A<>();
A<? extends Person> personA = maleA;
personA.test(female);

同样可以通过编译,但这样就违反了设计泛型的初衷,破坏了类型安全性。所以java编译器干脆就拒绝编译这样的代码了

是无法执行还是无法通过编译,泛型不会影响执行,编译成字节码之后,所有调用的参数都会变成Person,调用不可能不成功。

如果编译不能成功,可以强制类型转换。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题