Ho creato un metodo con argomenti tipo, restituendo un tipo generico utilizzando questi argomenti di tipo e prendendo argomenti Function
che dipende anche dagli argomenti tipo. Quando uso lambdas come argomenti, il compilatore mi obbliga a specificare gli argomenti di tipo del metodo, il che sembra sbagliato.Impossibile utilizzare il metodo Java 8 con argomenti lambda senza specificare argomenti tipo
Sto progettando una classe di utilità con i metodi da utilizzare con Stream.flatMap
. Gestisce ogni tipo di voce raccolta in un oggetto FlatEntry che contiene un elemento chiave e valore e può farlo su più livelli con un costruttore. Il metodo interessato è flatEntryMapperBuilder
. Ecco il codice:
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;
}
}
}
Il problema viene fornito con il suo utilizzo. Dire che ho:
Map<String, Set<String>> level1Map;
Posso tracciare ogni elemento sub set ad un FlatEntry facendo:
level1Map.entrySet().stream().flatMap(GdkStreams.flatEntryMapper(Entry::getKey, entry -> entry.getValue().stream()));
e funziona bene. Ma quando provo a fare questo:
level1Map.entrySet()
.stream()
.flatMap(GdkStreams.flatEntryMapperBuilder(Entry::getKey, entry -> entry.getValue().stream()).build());
L'eclisse (Marte 4.5.0) Soggiorni compilatore con:
- 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>
E javac (1.8.0_51) rompe con:
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
Se sostituisco Entry::getKey
per entry -> entry.getKey()
, javac cambia drasticamente l'output:
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
Si compila bene specificando parametri di tipo, che è quello che mi aspettavo:
level1Map.entrySet()
.stream()
.flatMap(GdkStreams.<Entry<String, Set<String>>, String, String> flatEntryMapperBuilder(Entry::getKey,
entry -> entry.getValue()
.stream())
.build());
o specificando uno degli argomenti digitare parametri:
Function<Entry<String, Set<String>>, String> keyGetter = Entry::getKey;
level1Map.entrySet()
.stream()
.flatMap(GdkStreams.flatEntryMapperBuilder(keyGetter, entry -> entry.getValue().stream()).build());
Ma questo è goffo! Immaginate ora come goffa sarebbe di scrivere tutti i parametri di tipo con 2 livelli nella mappa, utilizzando il metodo a catena (che è il mio utilizzo di destinazione):
Map<String, Map<String, Set<String>>> level2Map;
Ho letto molte altre domande su lambda e generici inferenza ma nessuno risponde al mio caso particolare.
mi sto perdendo qualcosa? Posso correggere la mia API in modo che il suo utilizzo sia meno impacciato o sono sempre bloccato a specificare gli argomenti di tipo? Grazie!
Nel codice attuale si hanno due più argomenti tipo k2, v2, dovrebbe essere K e V? –
Il metodo 'chain' ha più argomenti tipo, ma per il caso d'uso presentato non è usato (l'ho incluso per completezza). Il problema si verifica solo usando 'flatEntryMapperBuilder'. Rimuovere 'chain' dal tipo' FlatEntryMapperBuilder' non cambia il problema. –
Questa è una limitazione nota dell'inferenza di tipo di Java 8: non funziona con invocazioni di metodi concatenati come 'genericFactoryMethod(). Build()'. – Holger