目前用的是 jdk8 144 版本,写了如下一个方法
Map<String, Object> map = new HashMap<>();
map.put("a", new ArrayList<String>());
methodA((Collection) map.get("a")).stream()
.forEach(p -> System.out.println(p.getClass()));
methodA((Collection<String>) map.get("a")).stream()
.forEach(p -> System.out.println(p.getClass()));
如下为 methodA
List<String> l = new ArrayList<>();
l.add("a");
l.add("b");
return l;
两端输出都是 String 类型, 然而推断类型的时候,第一个调用的参数 p 为 Object,第二个为 String。。。
我感觉写的代码有点误导大家了。。我再重新写下
定义一个对象Aoo
class Aoo {
int a;
public int getA() {
return a;
}
}
再定义一个方法
public List<Aoo> methodA(Collection<String> c) {
List<Aoo> l = new ArrayList<>();
l.add(new Aoo());
return l;
}
然后调用methodA
Map<String, Object> map = new HashMap<>();
map.put("a", new ArrayList<String>());
//第一种调用
methodA((Collection) map.get("a")).stream()
.forEach(p -> //这里编译器认为p是Object);
//第二种调用
methodA((Collection<String>) map.get("a")).stream()
.forEach(p -> //这里编译器认为p是Aoo);
这样第一种调用是没法直接调用方法getA()的,
所以我就疑问为什么方法签名的泛型指定会影响到方法返回值的类型
1
openthinks 2019-01-15 17:22:00 +08:00
楼主期望输出是什么?
String s1 = "xx"; Object o1="xx"; s1.getClass() o1.getClass() |
2
mazai 2019-01-15 17:31:32 +08:00
没明白你的意思
|
3
anzu 2019-01-15 17:44:07 +08:00
没问题呀
|
4
rizon 2019-01-15 18:10:18 +08:00
你没贴 methodA 的返回值类型啊。方法返回值决定了后续操作的对象类型啊
|
5
bumz 2019-01-15 18:42:17 +08:00
你的 methodA 是这样的吗?
``` private static <E> Collection<E> methodA(Collection<E> a) { List<String> l = new ArrayList<>(); l.add("a"); l.add("b"); return (Collection<E>) l; } ``` 这样就重现了你描述中的情景 然而这不是很正常吗 |
7
beijiaxu OP @openthinks 不是什么期望输出,只是我在 lambda 函数里变量推导的类型可能是 Object,可能是具体的我要的类型。。
|
8
beijiaxu OP 可能大家都没太懂,我再写下。。
首先有个如下方法 methodA List<String> methodA(Collection<String> c) {return ...} 然后调用该方法,使用 map 来获得变量 Map<String, Object> map = new HashMap<>(); map.put("a", new ArrayList<String>()); 第一种方式:强转类型不加泛型类型 methodA( (Collection) map.get("a") ) .stream().forEach(p -> 这里的参数 p 推导类型为 Object ) 第二种方式:强转类型有泛型类型 methodA( (Collection<String>) map.get("a") ) .stream().forEach(p -> 这里的参数 p 推导类型为 String ) 我不明白的是,为什么方法签名的泛型会影响到 lambda 函数推导方法返回值的类型,我已经在方法返回值里指定了泛型类型了呀。 |
9
beijiaxu OP 因为今天正好碰到了这个问题,我偷懒没个方法签名加集合的泛型,导致 lambda 里推导出的参数调用方法编译错误,然后我加了给集合加了个<String>, 就能正常工作了,感觉好奇怪。
|
10
chocotan 2019-01-15 20:49:03 +08:00
楼主所说的"推断类型"是指啥?
编译错误? IDE 的提示? |
11
cyspy 2019-01-15 20:52:22 +08:00
Collection 不加参数默认是 Collection<Object> ,编译器不知道里面的类型
|
12
m2276699 2019-01-15 20:59:13 +08:00
符合预期
|
13
WangYanjie 2019-01-15 23:33:09 +08:00
interface Collection<?>{} 是 generic type.
Collection<String> 是 parameterized type, an instance of generic type `Collection` Collection 是 raw type, java 5 之前的产物 Collection<?> 是 unbound wildcard parameterized type type erasure 的 **一部分** 是指编译器会在编译期间,把 parameterized type 中的 type parameter 都消去, Collection<String>, Collection, Collection<?> 应该都是对应的一种运行时的类 尝试了以下并没有你这样的效果。 import java.util.*; public class Demo { public static List<String> methodA(Collection<String> cs) { List<String> stringList = new ArrayList<>(); for (String s : cs) { stringList.add(s); } return stringList; } public static void main(String args[]) { Map<String, Object> map = new HashMap<>(); List<String> stringList = new ArrayList<>(); stringList.add("s"); map.put("A", stringList); methodA((Collection) map.get("A")).stream().forEach(p -> System.out.println(p.getClass())); methodA((Collection<String>) map.get("A")).stream().forEach(p -> System.out.println(p.getClass())); } } class java.lang.String class java.lang.String ➜ ~ java -version java version "1.8.0_181" Java(TM) SE Runtime Environment (build 1.8.0_181-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode) |
14
cassia 2019-01-15 23:46:09 +08:00
类型擦除,了解下
|
15
a123321456b 2019-01-16 08:36:22 +08:00 via Android
Collection 可以当做 Collection<Object> 里面的 p 自然也是 Object 因为 java 有类型擦除所以得不到原始的类型 可以运行时判断 p instanceof String == true
|
16
mazai 2019-01-16 08:49:48 +08:00 via iPhone
第一种你 get 到的是 object 类型,强转的时候也没有指明,所以是 object
|
17
beijiaxu OP @WangYanjie 并不是期望输出 class type...这个 2 种调用输出都一样。
想问的是在 lambda 函数里参数 p 的编译时推导的类型,一个是 Object,一个是 String,所以参数 p 调用方法时若不指定 methodA 签名里的泛型,会需要用到强转,否则编译错误。给出的提示可用方法只有 Object 的方法,String 的方法一个都没有。 |
18
mezi04 2019-01-16 10:12:33 +08:00
建议楼主看看 collection 的源码。methodB 指定了 Collection 泛型的类型,编译器自然可以推断出 p 的类型了
|
19
WangYanjie 2019-01-16 21:55:09 +08:00
@beijiaxu 看错题了,尴尬
我理解的,明天去学习下能不能看到编译后的代码的 1 泛型在编译期间会有 type erasure 的过程,会导致 Collection<String> 等价于 Collection<Object>, 2 在 type erasure 的过程中,编译器会按需要自动加上 |
20
WangYanjie 2019-01-16 22:28:04 +08:00
@WangYanjie
类型转换 3 Collection 是 raw type, 不参与 type erasure 一种猜想是 type ensure 的过程中加了 type cast, 一种猜想是 type ensure 的过程中加了 bridge method. 我倾向与后者 当使用 Collection 时,lambda 编译时对应的是 A interface Consumer<T> { accept(T o) } class A implements Consumer { accept(Object o) {} } 编译后 interface Consume { accept(Object o) } class A implements Consumer { accept(Object o) {} } 当使用 Collection<String> 时,lambda 编译时对应的是 A class Consumer<T> { accept(Object o) } class A implements Consumer<String> { accept(String o) {} } 编译后会增加 i 一个 bridge method class Consumer { accept(Object o) } class A implements Consumer<String> { accept(String o) {} accept(Object o) {this.accept((String) o)} } 主要的信息来源是 Java Generic FAQs: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ102 从头阅读风味更佳 |
21
beijiaxu OP @WangYanjie 我又重新写了下例子,感觉都误导到大家了。。。
|
22
j2gg0s 2019-01-18 00:47:26 +08:00
@beijiaxu 我发现我有两个号,另一个叫 WangYanjie
昨天是我发现我后面不能自圆其说了,今天仔细看了下。 直接的原因是因为:调用 methodA 时如果有 unchecked conversation,会导致返回类型为定义返回类型的擦除后类型。 所以 methodA(Collection<String>) 返回 List<String>; methodA(Collection) 返回 List ; 具体可以看 https://segmentfault.com/a/1190000017935037,我整理在此了 |