《Java 8 in Action》中Collectors.joining()无需map的说法存疑
关于《Java 8 in Action》中Collectors.joining()示例的问题解答
首先得给你明确:书中的这段示例代码存在笔误,你实际测试后发现必须添加map(Person::toString)才能运行的结果是完全正确的,接下来咱们一步步拆解问题。
为什么原示例无法编译运行?
核心原因是对Collectors.joining()的方法签名理解偏差。我们来看无参版joining()的定义:
public static Collector<CharSequence, ?, String> joining()
这个方法要求流中的元素必须是CharSequence接口的实现类(最典型的就是String),而你的Person类哪怕重写了toString,本身也不属于CharSequence子类,编译器自然会报错,拒绝把Stream<Person>传给只接受Stream<CharSequence>的收集器。
关于toString继承的疑问解答
你提到“所有对象都默认继承Object的toString方法,但书中说无需map”,这里是书中表述的歧义导致的误解:
- 没错,所有对象都自带
toString()方法,但Collectors.joining()并不会自动帮你调用这个方法完成类型转换——它只认CharSequence类型的元素,不会主动把任意对象转成字符串。 - 书中想表达的真实意思应该是:如果类重写了toString,那么你不需要额外提取对象的某个属性(比如
map(Person::getName)),直接用map(Person::toString)就能得到包含对象信息的字符串,而不是说可以完全省略map转换步骤。
正确的可运行代码示例
你的修正版本才是符合Java语法的正确写法:
public static void main(String... args) { List<Person> people = Arrays.asList( new Person(23, "Paul"), new Person(23, "John"), new Person(23, "Greg"), new Person(24, "Greg"), new Person(25, "Paul") ); // 必须显式将Person对象转换为String(CharSequence的子类) String peopleString = people .stream() .map(Person::toString) // 调用重写后的toString方法 .collect(Collectors.joining()); System.out.println(peopleString); }
假设你的Person类重写了toString(比如返回"Person{age=23, name='Paul'}"),这段代码就会把所有对象的字符串表示拼接成一个完整字符串输出。
补充:另一种无需显式map的写法?
其实Collectors.joining()有重载版本可以间接完成转换,但本质还是需要指定对象转字符串的逻辑,并没有省略核心转换步骤:
String peopleString = people .stream() .collect(Collectors.mapping(Person::toString, Collectors.joining()));
这种写法和显式map的效果完全一致,只是把转换逻辑放到了收集器内部。
内容的提问来源于stack exchange,提问作者Coder-Man
相关产品推荐
相关产品推荐

