调用Stream的构造器方法(Stream.builder)为何无法推断泛型类型?
Java版本:jdk8以上
复现代码:
// 为什么我传了1,还是推断的Object类型?
Stream<Object> build = Stream.builder().add(1).build();
// 这行报错
Stream<Integer> build2 = Stream.builder().add(1).build();
// 这行可以,为什么要指定Integer泛型?
Stream<Integer> build3 = Stream.<Integer>builder().add(1).build();
看了网上的一些关于泛型构造器文章,还是不知道怎么理解这块这个问题我纠结了挺久的>_<各位大侠有没有心得指点一下Orz
已解决
因为调用构造方法的时候没有传具体参数的原因导致编译器默认泛型为Object。下面这个demo复现了相同的问题:
public class MySSTest<T> {
T data;
public MySSTest() {
}
public MySSTest(T data) {
this.data = data;
}
MySSTest<T> method(T param) {
data = param;
return this;
}
static <T> MySSTest<T> getBuilder() {
return new MySSTest<>();
}
static <T> MySSTest<T> getBuilder(T param) {
return new MySSTest<>(param);
}
public static void main(String[] args) {
MySSTest<Object> builder = MySSTest.getBuilder();
MySSTest<Integer> builder1 = MySSTest.getBuilder(1);
MySSTest<Object> method = MySSTest.getBuilder().method(1);
MySSTest<Integer> method1 = MySSTest.getBuilder(1).method(2);
}
}
延展
关于一个泛型类的泛型构造方法的讨论,此时有一个类如下:
public class MyTest<T, R> {
T data;
R member;
public MyTest() {
}
public MyTest(T data) {
this.data = data;
}
public <U> MyTest(T data, U param, R member) {
this.data = data;
this.member = member;
System.out.println(data + ", " + param);
}
static <T, R> MyTest<T, R> getBuilder() {
return new MyTest<>();
}
public static void main(String[] args) {
// 从这一行可以看出,调用泛型构造器时,构造器方法左边的泛型参数对应的是入参泛型,方法右边则是类的泛型
MyTest<Integer, String > integerMyTest = new <Boolean>MyTest<Integer, String>(233, true, "123");
// 当入参泛型不指定时,类的泛型也可以省略但不能省略菱形括号
MyTest<Integer, String > integerMyTest1 = new MyTest<>(233, true, "123");
// 当入参泛型指定时,类的泛型不能省略。 但是反过来类的类型指定时,入参泛型是可以省略的
MyTest<Integer, String> integerMyTest1 = new <Boolean>MyTest<>(233, true, "123");
}
}
此时再回顾Stream.builder()方法:
public static<T> Builder<T> builder() {
return new Streams.StreamBuilderImpl<>();
}
所以调用静态方法Stream.<Integer>builder()时,<Integer>是作为入参泛型类型传递,虽然此时没有入参,但是已经能指定该泛型方法的泛型类型了,又因为返回值是该对象泛型,所以后续操作的对象内容也都是Integer类型。
回复
1个回答

test
2024-07-14
如果你拆分开来,你会发现是能够推断的。
Stream.Builder<Integer> builder = Stream.builder();
Stream<Integer> add = builder.add(1).build();
看一下 builder
源码,结合上面的代码看,你就能知道推断泛型的逻辑是通过被赋值的泛型推断出来的。
public static<T> Builder<T> builder() {
return new Streams.StreamBuilderImpl<>();
}
那如果你连起来呢?因为这个过程是连续的,没有给 Stream.builder()
机会去推断类型,那就只能用 object
了,虽然你后面给了 add(1)
,但是上一步已经泛型被确定为 object
了,给 add(1)
不能改变什么,因为给 add(1)
是后面发生的,泛型类型是上一步确定的。
Stream.builder().add(1).build()
连起来的代码等价于:
Stream.Builder<Object> builder = Stream.builder();
builder = builder.add(1);
Stream<Object> stream = builder.build();
那么再看这一行你就能明白了,Stream.<Integer>builder()
这一步已经提前确定了泛型。
Stream<Integer> build3 = Stream.<Integer>builder().add(1).build();
最后给你一段我模仿的测试代码调试玩玩:
public class Test {
static class Stream<T> {
interface Builder<T> {
Builder<T> add(T t);
}
public static <T> Builder<T> builder() {
return new Streams.StreamBuilderImpl<>();
}
}
static class Streams {
static class StreamBuilderImpl<T> implements Stream.Builder<T> {
StreamBuilderImpl() {
}
@Override
public Stream.Builder<T> add(T t) {
return this;
}
}
}
public static void main(String[] args) {
Stream.Builder<Integer> builder1 = Stream.builder();
Stream.Builder<Integer> add = builder1.add(2);
Stream.Builder<Object> builder = Stream.builder().add(1);
}
}
补充
不能推断泛型,不是 stream 原因,如果你换一种方式使用 stream,你会发现实际上是能够推断的
Stream<Integer> stream = Stream.of(1);
你这段代码不能推断的根本原因,只是实现调用方式的问题。
回复

适合作为回答的
- 经过验证的有效解决办法
- 自己的经验指引,对解决问题有帮助
- 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
- 询问内容细节或回复楼层
- 与题目无关的内容
- “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容