如果未指定类型参数,则无法将Java 8方法与lambda参数一起使用

我创建了一个带有类型实参的方法,使用这些类型实参返回一个通用类型,并获取Function也取决于类型实参的参数。当我使用lambda作为参数时,编译器会强迫我指定方法的类型参数,这感觉不对。

我正在设计一个实用程序类,其中包含要与一起使用的方法Stream.flatMap。它将每种集合条目映射到包含键和值元素的FlatEntry,并且可以使用构建器在多个级别上执行此操作。受影响的方法是flatEntryMapperBuilder。这是代码:

import java.util.function.Function;

import java.util.stream.Stream;

public class GdkStreams

{

public static <T, K, V> Function<T, Stream<FlatEntry<K, V>>> flatEntryMapper(Function<T, K> keyMapper,

Function<T, Stream<V>> valueMapper)

{

return input -> {

K key = keyMapper.apply(input);

return valueMapper.apply(input).map(value -> new FlatEntry<>(key, value));

};

}

public static <T, K, V> FlatEntryMapperBuilder<T, K, V> flatEntryMapperBuilder(Function<T, K> keyMapper,

Function<T, Stream<V>> valueMapper)

{

return new FlatEntryMapperBuilder<>(keyMapper, valueMapper);

}

public static class FlatEntryMapperBuilder<T, K, V>

{

private Function<T, K> keyMapper;

private Function<T, Stream<V>> valueMapper;

private FlatEntryMapperBuilder (Function<T, K> keyMapper, Function<T, Stream<V>> valueMapper)

{

this.keyMapper = keyMapper;

this.valueMapper = valueMapper;

}

public Function<T, Stream<FlatEntry<K, V>>> build()

{

return flatEntryMapper(keyMapper, valueMapper);

}

public <K2, V2> FlatEntryMapperBuilder<T, K, FlatEntry<K2, V2>> chain(Function<V, K2> keyMapper2,

Function<V, Stream<V2>> valueMapper2)

{

return new FlatEntryMapperBuilder<>(keyMapper,

valueMapper.andThen(stream -> stream.flatMap(flatEntryMapper(keyMapper2,

valueMapper2))));

}

}

public static class FlatEntry<K, V>

{

public final K key;

public final V value;

public FlatEntry (K key, V value)

{

this.key = key;

this.value = value;

}

}

}

问题在于它的用法。说我有:

Map<String, Set<String>> level1Map;

我可以通过以下操作将子集中的每个元素映射到FlatEntry:

level1Map.entrySet().stream().flatMap(GdkStreams.flatEntryMapper(Entry::getKey, entry -> entry.getValue().stream()));

而且效果很好。但是当我尝试这样做时:

level1Map.entrySet()

.stream()

.flatMap(GdkStreams.flatEntryMapperBuilder(Entry::getKey, entry -> entry.getValue().stream()).build());

Eclipse(Mars 4.5.0)编译器的损坏之处在于:

- The type Map.Entry does not define getKey(Object) that is applicable here

- The method getValue() is undefined for the type Object

- Type mismatch: cannot convert from GdkStreams.FlatEntryMapperBuilder<Object,Object,Object> to

<unknown>

而javac(1.8.0_51)则打破了:

MainTest.java:50: error: incompatible types: cannot infer type-variable(s) T,K#1,V#1

.flatMap(GdkStreams.flatEntryMapperBuilder(Entry::getKey, entry -> entry.getValue().stream()).build());

^

(argument mismatch; invalid method reference

method getKey in interface Entry<K#2,V#2> cannot be applied to given types

required: no arguments

found: Object

reason: actual and formal argument lists differ in length)

where T,K#1,V#1,K#2,V#2 are type-variables:

T extends Object declared in method <T,K#1,V#1>flatEntryMapperBuilder(Function<T,K#1>,Function<T,Stream<V#1>>)

K#1 extends Object declared in method <T,K#1,V#1>flatEntryMapperBuilder(Function<T,K#1>,Function<T,Stream<V#1>>)

V#1 extends Object declared in method <T,K#1,V#1>flatEntryMapperBuilder(Function<T,K#1>,Function<T,Stream<V#1>>)

K#2 extends Object declared in interface Entry

V#2 extends Object declared in interface Entry

MainTest.java:50: error: invalid method reference

.flatMap(GdkStreams.flatEntryMapperBuilder(Entry::getKey, entry -> entry.getValue().stream()).build());

^

non-static method getKey() cannot be referenced from a static context

where K is a type-variable:

K extends Object declared in interface Entry

2 errors

如果我替换Entry::getKeyentry -> entry.getKey(),则javac将彻底改变其输出:

MainTest.java:51: error: cannot find symbol

.flatMap(GdkStreams.flatEntryMapperBuilder(entry -> entry.getKey(), entry -> entry.getValue().stream()).build());

^

symbol: method getKey()

location: variable entry of type Object

MainTest.java:51: error: cannot find symbol

.flatMap(GdkStreams.flatEntryMapperBuilder(entry -> entry.getKey(), entry -> entry.getValue().stream()).build());

^

symbol: method getValue()

location: variable entry of type Object

2 errors

通过指定类型参数可以很好地进行编译,这是我期望的:

level1Map.entrySet()

.stream()

.flatMap(GdkStreams.<Entry<String, Set<String>>, String, String> flatEntryMapperBuilder(Entry::getKey,

entry -> entry.getValue()

.stream())

.build());

或指定参数类型参数之一:

Function<Entry<String, Set<String>>, String> keyGetter = Entry::getKey;

level1Map.entrySet()

.stream()

.flatMap(GdkStreams.flatEntryMapperBuilder(keyGetter, entry -> entry.getValue().stream()).build());

但这很笨拙!现在想象一下,使用chain方法(这是我的目标用法)在地图中写入具有2个级别的所有类型参数会多么笨拙:

Map<String, Map<String, Set<String>>> level2Map;

我已经阅读了许多其他有关lambda和泛型类型推断的问题,但没有一个问题可以回答我的特殊情况。

我想念什么吗?我可以更正我的API以便它的用法不太笨拙,还是我始终使用类型参数?谢谢!

回答:

在我看来,Holger在评论部分给出了最佳答案:

这是Java 8类型推断的一个已知限制:它不适用于链式方法调用genericFactoryMethod().build()

谢谢!关于我的API,我将在使用函数作为参数之前指定它们,如下所示:

Function<Entry<String, Set<String>>, String> keyMapper = Entry::getKey;

Function<Entry<String, Set<String>>, Stream<String>> valueMapper = entry -> entry.getValue().stream();

编辑:由于霍尔格的评论,我重新设计了API(再次感谢!)。它保留原始元素而不是键,以及展平的值。

public static <T, R> Function<? super T, Stream<FlatEntry<T, R>>> flatEntryMapper(Function<? super T, ? extends Stream<? extends R>> mapper)

{

return element -> mapper.apply(element).map(value -> new FlatEntry<>(element, value));

}

public static class FlatEntry<E, V>

{

/** The original stream element */

public final E element;

/** The flattened value */

public final V value;

private FlatEntry (E element, V value)

{

this.element = element;

this.value = value;

}

}

它是可链接的,从2级开始,映射器必须处理a FlatEntry。用法类似于简单的用法flatMap

Map<String, Map<String, Map<String, Set<String>>>> level3Map;

// gives a stream of all the flattened values

level3Map.entrySet()

.stream()

.flatMap(entry -> entry.getValue().entrySet().stream())

.flatMap(entry -> entry.getValue().entrySet().stream())

.flatMap(entry -> entry.getValue().stream());

// gives a stream of FlatEntries with flattened values and all their original elements in nested FlatEntries

level3Map.entrySet()

.stream()

.flatMap(GdkStreams.flatEntryMapper(entry -> entry.getValue().entrySet().stream()))

.flatMap(GdkStreams.flatEntryMapper(flatEntry -> flatEntry.value.getValue().entrySet().stream()))

.flatMap(GdkStreams.flatEntryMapper(flatEntry -> flatEntry.value.getValue().stream()));

以上是 如果未指定类型参数,则无法将Java 8方法与lambda参数一起使用 的全部内容, 来源链接: utcz.com/qa/414286.html

回到顶部