我创建了一个带有类型实参的方法,使用这些类型实参返回一个通用类型,并获取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::getKey
为entry -> 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()));
我可以通过执行以下操作将子集中的每个元素映射到一个FlatEntry: 而且效果很好。但当我尝试这样做时: eclipse(Mars4.5.0)编译器的功能如下: 我是不是漏掉了什么?我能纠正我的API,使它的使用不那么笨拙吗?还是我一直在指定类型参数?谢谢!
问题内容: 我刚刚开始学习Java流,并遇到了问题。请看下面的例子。这是Node类的一部分: 我的意图是在流中的每个节点上使用名称和结果参数调用#findChildren。我尝试使用方法引用Node :: findChildren时没有运气。我将不胜感激的解决方案与运营商。 是否可以将方法引用与参数一起使用?我喜欢流的想法,我只想使代码更具可读性。 实际上,我认为还有一个类似的问题,方法引用具有读
你好,我在小部件上的以下代码中出错 参数类型'List 这是我的代码 我的代码与上面的图表插件不兼容吗?如何解决这个问题?
主要内容:1 Java8 方法参数反射的介绍,2 Method类,3 Method类的方法,4 Parameter类,5 Parameter类的方法,6 Java8 方法参数反射的案例1 Java8 方法参数反射的介绍 Java提供了一项新功能,您可以在其中获得任何方法或构造函数的形式参数的名称。java.lang.reflect包包含所有必需的类,例如Method和Parameter,可用于参数反射。 2 Method类 Method类提供有关类或接口上的单个方法的信息。反射的方法可以是类方法
我对这个flutter简单图表代码有问题。在我尝试运行代码时显示此错误。有人能帮我吗...... 参数类型'List 这是代码示例:
我对弗利特和我是新手;我一直在尝试开发一款带有PHP API后端的应用程序。在无数的答案和在线研究中,我无法找到解决我所面临问题的方法。我希望有人能告诉我我做错了什么。 我试图从API中获取数据,并将其显示在卡中。下面是我的代码: 希望能得到一些帮助。非常感谢。