Java 泛型方法与多态,这样解释对吗

2019-09-07 16:33:04 +08:00
 amiwrong123
class A{}
class B extends A{}

class GenericMethods {
    public <T> T f1(T x) {
        System.out.println(x.getClass().getName());
        return x;
    }
}

public class testP {
    public static void main(String[] args) {
        GenericMethods gm = new GenericMethods();
        A a = gm.f1(new B());
        //B b = gm.f1(new A());编译报错
    }
}

f1 方法是一个泛型方法,返回值是< >里的T,形参也是< >里的T。所以二者推断出来的具体类型必须一样,或者符合多态。

执行A a = gm.f1(new B())时,返回值处的T被推断为A,形参处的T本来会被推断为B,但是由于前者,形参处的T这里被推断为A。这里传B对象作为实参符合多态,泛型和多态不冲突。

还有就是 B b = gm.f1(new A());编译通不过怎么解释比较好,可以认为这句是推断为B返回的也是B只是这里不允许向下转型吗?

6100 次点击
所在节点    Java
28 条回复
hantsy
2019-09-07 16:39:03 +08:00
牛马不相及的两个东西。
Raymon111111
2019-09-07 16:42:55 +08:00
B 是一个 A

而 A 却不是一个 B

换个简单的说法, 有一个装水果的篮子可以装苹果, 但是有一个装苹果的篮子肯定不能装水果, 因为这个水果可能是香蕉.
Bromine0x23
2019-09-07 16:44:00 +08:00
类型参数只能从方法参数推导的
`gm.f1(new B())` 就推导成 `B f1(B)`,`gm.f1(new A())` 就推导成 `A f1(A)`
amiwrong123
2019-09-07 16:53:31 +08:00
@Bromine0x23
可是我觉得类型参数也能通过返回值处的类型参数推断啊。比如:
```java
class GenericMethods {
public <T> T f2(Object x) {
System.out.println(x.getClass().getName());
return (T)x;
}
}

public class testP {
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
int o1 = gm.f2(2);
String o2 = gm.f2(2);//能通过编译,但运行时报错
}
}
```
这里可以认为,执行`String o2 = gm.f2(2)`后,由于这里`T`就被推断为了`String`,一个实际为 2 的 Object 对象在向上转型为`String`类型后,执行(String)x,这句会受到 RTTI 的检查,被发现无法转型后便报错。
Bromine0x23
2019-09-07 17:31:03 +08:00
@amiwrong123
详细说的话是 f2 的 T 参数在信息不足的情况下被推延了。
这个推导是分段进行的,首先是对 gm.f2(2),由于 T 没出现在参数中所以这里不能确定(或者说可选范围是 <= Object )
然后 int o1 = <T> 或者 String o2 = <T>,在就能确定 T 的类型了

对前面 B b = gm.f1(new A()), 在 gm.f1(new A()) 中已经能确定 T = A 了,所以之后 B b = <A> 的表达式由于类型不匹配导致编译失败
amiwrong123
2019-09-07 17:38:33 +08:00
@Bromine0x23
谢谢回答。但 A a = gm.f1(new B());这里该怎么理解呢,照你这么说,意思就是,有了方法参数的推断,就不需要返回值的推断了。那这里 f1 的 T 就被推断为 B 了呗。

只是 gm.f1(new B())返回了一个 B 对象,然后由于赋值,向上转型为了一个 A 对象。
Bromine0x23
2019-09-07 17:40:31 +08:00
@amiwrong123 是的
cigarzh
2019-09-07 19:42:12 +08:00
你这报错和泛型有啥关系……
amiwrong123
2019-09-07 19:59:42 +08:00
@cigarzh
主要类型参数推断理解错了
axlecho
2019-09-07 21:08:01 +08:00
A a = new B ok
B b = new A failed
这里跟泛型没关系
fengpan567
2019-09-07 21:17:00 +08:00
和泛型没关系。这个是 B 是子类,A 是父类,父类的实例引用不能指向子类,但是子类实例引用是可以指向父类的
ninjachen
2019-09-07 21:52:24 +08:00
乡下转型。。。
这个是什么词汇?
只听过强行 cast,向下是不可能自动做的
ninjachen
2019-09-07 21:52:45 +08:00
乡下 typo,是向下
amiwrong123
2019-09-07 23:02:45 +08:00
@ninjachen
不好意思,创造了个新词汇 囧 rz
jxie0755
2019-09-08 09:01:16 +08:00
泛型和多态好像没有什么很相关的地方? 虽然都是面向对象编程里的概念
泛型是针对容器做出的设定
多态是针对继承关系做出的设定
如果你一定要对几个容器之间做继承关系的话.........那你参考下 Collection 和 Arraylist 之间是怎么个安排的吧?
lolizeppelin
2019-09-08 10:57:58 +08:00
你先用 python 之类的动态语言写一遍

再用 java 这样的静态语言写一遍

然后你就理解为什么静态语言需要泛型这玩意了

不要死脑筋去理解,功能做出来是为了解决痛点的,你知道为什么痛了,自己然就懂了
amiwrong123
2019-09-08 12:20:55 +08:00
@Bromine0x23
大佬,能否再帮忙看下附言 2 的疑问,感谢!
是不是成员泛型方法的 T 被泛型类的 T 覆盖掉了。
oneisall8955
2019-09-08 12:46:14 +08:00
我写了个博客,https://liuzhicong.cn/index.php/study/extends-T-and-super-T.html,
想看精简版本,看借鉴参考的第二篇 stackoverflow 的就行了,私认为解释的很清楚
amiwrong123
2019-09-08 13:25:24 +08:00
@oneisall8955
是通配符方面的知识哈,等我看书看到这块再去观摩你的博客把。
话说层主能否帮看下附言 2,不知道我的理解对吗。。
Bromine0x23
2019-09-08 13:29:44 +08:00
@amiwrong123
你声明的 gen 是 Generator 而不是 BasicGenerator,当然调用不了 create1 和 test

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/598864

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX