How does this compile?
Clash Royale CLAN TAG#URR8PPP
up vote
12
down vote
favorite
I'm writing a function that takes a list of keyExtractor functions to produce a Comparator (imagine we had an object with many many properties and wanted to be able to arbitrarily compare by a large number of properties in any order).
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
class Test
public static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors)
if (keyExtractors.isEmpty())
return (a, b) -> 0;
else
Function<T, S> firstSortKey = keyExtractors.get(0);
List<Function<T, S>> restOfSortKeys = keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey).thenComparing(parseKeysAscending(restOfSortKeys));
public static void main(String args)
List<Extractor<Data, ?>> extractors = new ArrayList<>();
extractors.add(new Extractor<>(Data::getA));
extractors.add(new Extractor<>(Data::getB));
Comparator<Data> test = parseKeysAscending(
extractors.stream()
.map(e -> e)
.collect(Collectors.toList()));
class Extractor<T, S extends Comparable<S>> implements Function<T, S>
private final Function<T, S> extractor;
Extractor(Function<T, S> extractor)
this.extractor = extractor;
@Override
public S apply(T t)
return extractor.apply(t);
class Data
private final Integer a;
private final Integer b;
private Data(int a, int b)
this.a = a;
this.b = b;
public Integer getA()
return a;
public Integer getB()
return b;
There are three main points of confusion for me:
1). If I don't define the Extractor class, this will not compile. I cannot directly have Functions or some sort of functional interface.
2). If I remove the identity function mapping line ".map(e -> e)", this will not type check.
3). My IDE says my function is accepting a List of Functions of the type Data -> ? which doesn't comply with the bounds of the parseKeysAscending function.
java generics java-8 functional-programming comparator
add a comment |Â
up vote
12
down vote
favorite
I'm writing a function that takes a list of keyExtractor functions to produce a Comparator (imagine we had an object with many many properties and wanted to be able to arbitrarily compare by a large number of properties in any order).
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
class Test
public static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors)
if (keyExtractors.isEmpty())
return (a, b) -> 0;
else
Function<T, S> firstSortKey = keyExtractors.get(0);
List<Function<T, S>> restOfSortKeys = keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey).thenComparing(parseKeysAscending(restOfSortKeys));
public static void main(String args)
List<Extractor<Data, ?>> extractors = new ArrayList<>();
extractors.add(new Extractor<>(Data::getA));
extractors.add(new Extractor<>(Data::getB));
Comparator<Data> test = parseKeysAscending(
extractors.stream()
.map(e -> e)
.collect(Collectors.toList()));
class Extractor<T, S extends Comparable<S>> implements Function<T, S>
private final Function<T, S> extractor;
Extractor(Function<T, S> extractor)
this.extractor = extractor;
@Override
public S apply(T t)
return extractor.apply(t);
class Data
private final Integer a;
private final Integer b;
private Data(int a, int b)
this.a = a;
this.b = b;
public Integer getA()
return a;
public Integer getB()
return b;
There are three main points of confusion for me:
1). If I don't define the Extractor class, this will not compile. I cannot directly have Functions or some sort of functional interface.
2). If I remove the identity function mapping line ".map(e -> e)", this will not type check.
3). My IDE says my function is accepting a List of Functions of the type Data -> ? which doesn't comply with the bounds of the parseKeysAscending function.
java generics java-8 functional-programming comparator
There does seem to be some wonky stuff going on here. Question worth asking to me, since my IDE also optimizes out the mapping but then immediately refuses to compile. What version of Java are you using?
â Makoto
3 hours ago
I am using jdk 1.8.0_181
â Billy the Kid
3 hours ago
add a comment |Â
up vote
12
down vote
favorite
up vote
12
down vote
favorite
I'm writing a function that takes a list of keyExtractor functions to produce a Comparator (imagine we had an object with many many properties and wanted to be able to arbitrarily compare by a large number of properties in any order).
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
class Test
public static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors)
if (keyExtractors.isEmpty())
return (a, b) -> 0;
else
Function<T, S> firstSortKey = keyExtractors.get(0);
List<Function<T, S>> restOfSortKeys = keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey).thenComparing(parseKeysAscending(restOfSortKeys));
public static void main(String args)
List<Extractor<Data, ?>> extractors = new ArrayList<>();
extractors.add(new Extractor<>(Data::getA));
extractors.add(new Extractor<>(Data::getB));
Comparator<Data> test = parseKeysAscending(
extractors.stream()
.map(e -> e)
.collect(Collectors.toList()));
class Extractor<T, S extends Comparable<S>> implements Function<T, S>
private final Function<T, S> extractor;
Extractor(Function<T, S> extractor)
this.extractor = extractor;
@Override
public S apply(T t)
return extractor.apply(t);
class Data
private final Integer a;
private final Integer b;
private Data(int a, int b)
this.a = a;
this.b = b;
public Integer getA()
return a;
public Integer getB()
return b;
There are three main points of confusion for me:
1). If I don't define the Extractor class, this will not compile. I cannot directly have Functions or some sort of functional interface.
2). If I remove the identity function mapping line ".map(e -> e)", this will not type check.
3). My IDE says my function is accepting a List of Functions of the type Data -> ? which doesn't comply with the bounds of the parseKeysAscending function.
java generics java-8 functional-programming comparator
I'm writing a function that takes a list of keyExtractor functions to produce a Comparator (imagine we had an object with many many properties and wanted to be able to arbitrarily compare by a large number of properties in any order).
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
class Test
public static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors)
if (keyExtractors.isEmpty())
return (a, b) -> 0;
else
Function<T, S> firstSortKey = keyExtractors.get(0);
List<Function<T, S>> restOfSortKeys = keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey).thenComparing(parseKeysAscending(restOfSortKeys));
public static void main(String args)
List<Extractor<Data, ?>> extractors = new ArrayList<>();
extractors.add(new Extractor<>(Data::getA));
extractors.add(new Extractor<>(Data::getB));
Comparator<Data> test = parseKeysAscending(
extractors.stream()
.map(e -> e)
.collect(Collectors.toList()));
class Extractor<T, S extends Comparable<S>> implements Function<T, S>
private final Function<T, S> extractor;
Extractor(Function<T, S> extractor)
this.extractor = extractor;
@Override
public S apply(T t)
return extractor.apply(t);
class Data
private final Integer a;
private final Integer b;
private Data(int a, int b)
this.a = a;
this.b = b;
public Integer getA()
return a;
public Integer getB()
return b;
There are three main points of confusion for me:
1). If I don't define the Extractor class, this will not compile. I cannot directly have Functions or some sort of functional interface.
2). If I remove the identity function mapping line ".map(e -> e)", this will not type check.
3). My IDE says my function is accepting a List of Functions of the type Data -> ? which doesn't comply with the bounds of the parseKeysAscending function.
java generics java-8 functional-programming comparator
java generics java-8 functional-programming comparator
edited 1 hour ago
Federico Peralta Schaffner
19.3k32862
19.3k32862
asked 3 hours ago
Billy the Kid
784
784
There does seem to be some wonky stuff going on here. Question worth asking to me, since my IDE also optimizes out the mapping but then immediately refuses to compile. What version of Java are you using?
â Makoto
3 hours ago
I am using jdk 1.8.0_181
â Billy the Kid
3 hours ago
add a comment |Â
There does seem to be some wonky stuff going on here. Question worth asking to me, since my IDE also optimizes out the mapping but then immediately refuses to compile. What version of Java are you using?
â Makoto
3 hours ago
I am using jdk 1.8.0_181
â Billy the Kid
3 hours ago
There does seem to be some wonky stuff going on here. Question worth asking to me, since my IDE also optimizes out the mapping but then immediately refuses to compile. What version of Java are you using?
â Makoto
3 hours ago
There does seem to be some wonky stuff going on here. Question worth asking to me, since my IDE also optimizes out the mapping but then immediately refuses to compile. What version of Java are you using?
â Makoto
3 hours ago
I am using jdk 1.8.0_181
â Billy the Kid
3 hours ago
I am using jdk 1.8.0_181
â Billy the Kid
3 hours ago
add a comment |Â
4 Answers
4
active
oldest
votes
up vote
3
down vote
accepted
It works for me without the Extractor
class and also without calling map(e -> e)
in the stream pipeline. Actually, streaming the list of extractors isn't needed at all if you use the correct generic types.
As to why your code doesn't work, I'm not completely sure. Generics is a tough and flaky aspect of Java... All I did was adjusting the signature of the parseKeysAscending
method, so that it conforms to what Comparator.comparing
actually expects.
Here's the parseKeysAscending
method:
public static <T, S extends Comparable<? super S>> Comparator<T> parseKeysAscending(
List<Function<? super T, ? extends S>> keyExtractors)
if (keyExtractors.isEmpty())
return (a, b) -> 0;
else
Function<? super T, ? extends S> firstSortKey = keyExtractors.get(0);
List<Function<? super T, ? extends S>> restOfSortKeys =
keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
And here's a demo with the call:
List<Function<? super Data, ? extends Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
Comparator<Data> test = parseKeysAscending(extractors);
List<Data> data = new ArrayList<>(Arrays.asList(
new Data(1, "z"),
new Data(2, "b"),
new Data(1, "a")));
System.out.println(data); // [[1, 'z'], [2, 'b'], [1, 'a']]
data.sort(test);
System.out.println(data); // [[1, 'a'], [1, 'z'], [2, 'b']]
The only way to make the code compile without warnings was to declare the list of functions as List<Function<Data, Integer>>
. But this works only with getters that return Integer
. I'm assuming that you might want to compare any mix of Comparable
s, i.e. the code above works with the following Data
class:
public class Data
private final Integer a;
private final String b;
private Data(int a, String b)
this.a = a;
this.b = b;
public Integer getA()
return a;
public String getB()
return b;
@Override
public String toString()
return "[" + a + ", '" + b + "']";
Here's the demo.
1
I still don't understand where the problem was or is (it compiled with java-11 that I have and use), but as a side note I think that method can be shorten topublic static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors) return keyExtractors.stream() .collect(Collector.of( () -> (Comparator<T>) (x, y) -> 0, Comparator::thenComparing, Comparator::thenComparing));
â Eugene
2 hours ago
Two interesting observations for your code Federico: 1). This doesn't work for me if I remove the stream in the call 2). I am getting a "cannot resolve method" in the recursive call yet it still successfully runs.
â Billy the Kid
2 hours ago
This looks like what I am going for. I think this is also a great example for me to understand contravariance/covariance in Java through the wildcard type. This may be IDE specific, but using the method as written gives me an unchecked method exception. If I use the intermediate "extractor" class as before, this warning goes away. Also, doesn't this still mean our method is accepting some type "S extends Comparable<? super S>"? I don't think there should be any way to call this with data that has both a string and integer field without getting the unchecked warning.
â Billy the Kid
42 mins ago
@BillytheKid Honestly, I'm not getting that exception/compilation error, so I cannot tell. Usually, Eclipse compiler is known to have issues with type inference (i.e. covariance/contravariance, generic types. lambdas and method references all mixed together). As to the unchecked warning, I don't think there's a way to get rid of it.
â Federico Peralta Schaffner
16 mins ago
add a comment |Â
up vote
3
down vote
After Federico corrected me (thank you!) this is a single method you could do it with:
public static <T, S extends Comparable<? super S>> Comparator<T> test(List<Function<T, S>> list)
return list.stream()
.reduce((x, y) -> 0,
Comparator::thenComparing,
Comparator::thenComparing);
And usage would be:
// I still don't know how to avoid this raw type here
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA); // getA returns an Integer
extractors.add(Data::getB); // getB returns a String
listOfSomeDatas.sort(test(extractors));
2
Very nice, this does make sense as the original recursive function follows a pretty standard fold-like pattern.
â Billy the Kid
19 mins ago
I still likelist.stream().map(Comparator::comparing).reduce(Comparator::thenComparing).orElse((Comparator<T>) (a, b) -> 0)
more than this, but +1 for keeping all this generics nightmare simple.
â Federico Peralta Schaffner
12 mins ago
add a comment |Â
up vote
1
down vote
- If I don't define the
Extractor
class, this will not compile. I cannot directly haveFunction
s or some sort of functional interface.
No, you can. You can define any Function<X, Y>
by a lambda, or method reference, or anonymous class.
List<Function<Data, Integer>> extractors = List.of(Data::getA, Data::getB);
- If I remove the identity function mapping line
.map(e -> e)
, this will not type check.
It still will, but the result may not be suitable for the method. You can always define generic parameters explicitly to make sure that everything goes as you expect.
extractors.<Function<Data, Integer>>stream().collect(Collectors.toList())
But there is no need here:
Comparator<Data> test = parseKeysAscending(extractors);
add a comment |Â
up vote
0
down vote
Regarding your original code in the question, you can remove Extractor
class and use a raw Comparable
:
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
@SuppressWarnings("unchecked")
Comparator<Data> test = parseKeysAscending(extractors);
P.S. I also don't know how to get rid of the raw type here...
add a comment |Â
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
It works for me without the Extractor
class and also without calling map(e -> e)
in the stream pipeline. Actually, streaming the list of extractors isn't needed at all if you use the correct generic types.
As to why your code doesn't work, I'm not completely sure. Generics is a tough and flaky aspect of Java... All I did was adjusting the signature of the parseKeysAscending
method, so that it conforms to what Comparator.comparing
actually expects.
Here's the parseKeysAscending
method:
public static <T, S extends Comparable<? super S>> Comparator<T> parseKeysAscending(
List<Function<? super T, ? extends S>> keyExtractors)
if (keyExtractors.isEmpty())
return (a, b) -> 0;
else
Function<? super T, ? extends S> firstSortKey = keyExtractors.get(0);
List<Function<? super T, ? extends S>> restOfSortKeys =
keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
And here's a demo with the call:
List<Function<? super Data, ? extends Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
Comparator<Data> test = parseKeysAscending(extractors);
List<Data> data = new ArrayList<>(Arrays.asList(
new Data(1, "z"),
new Data(2, "b"),
new Data(1, "a")));
System.out.println(data); // [[1, 'z'], [2, 'b'], [1, 'a']]
data.sort(test);
System.out.println(data); // [[1, 'a'], [1, 'z'], [2, 'b']]
The only way to make the code compile without warnings was to declare the list of functions as List<Function<Data, Integer>>
. But this works only with getters that return Integer
. I'm assuming that you might want to compare any mix of Comparable
s, i.e. the code above works with the following Data
class:
public class Data
private final Integer a;
private final String b;
private Data(int a, String b)
this.a = a;
this.b = b;
public Integer getA()
return a;
public String getB()
return b;
@Override
public String toString()
return "[" + a + ", '" + b + "']";
Here's the demo.
1
I still don't understand where the problem was or is (it compiled with java-11 that I have and use), but as a side note I think that method can be shorten topublic static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors) return keyExtractors.stream() .collect(Collector.of( () -> (Comparator<T>) (x, y) -> 0, Comparator::thenComparing, Comparator::thenComparing));
â Eugene
2 hours ago
Two interesting observations for your code Federico: 1). This doesn't work for me if I remove the stream in the call 2). I am getting a "cannot resolve method" in the recursive call yet it still successfully runs.
â Billy the Kid
2 hours ago
This looks like what I am going for. I think this is also a great example for me to understand contravariance/covariance in Java through the wildcard type. This may be IDE specific, but using the method as written gives me an unchecked method exception. If I use the intermediate "extractor" class as before, this warning goes away. Also, doesn't this still mean our method is accepting some type "S extends Comparable<? super S>"? I don't think there should be any way to call this with data that has both a string and integer field without getting the unchecked warning.
â Billy the Kid
42 mins ago
@BillytheKid Honestly, I'm not getting that exception/compilation error, so I cannot tell. Usually, Eclipse compiler is known to have issues with type inference (i.e. covariance/contravariance, generic types. lambdas and method references all mixed together). As to the unchecked warning, I don't think there's a way to get rid of it.
â Federico Peralta Schaffner
16 mins ago
add a comment |Â
up vote
3
down vote
accepted
It works for me without the Extractor
class and also without calling map(e -> e)
in the stream pipeline. Actually, streaming the list of extractors isn't needed at all if you use the correct generic types.
As to why your code doesn't work, I'm not completely sure. Generics is a tough and flaky aspect of Java... All I did was adjusting the signature of the parseKeysAscending
method, so that it conforms to what Comparator.comparing
actually expects.
Here's the parseKeysAscending
method:
public static <T, S extends Comparable<? super S>> Comparator<T> parseKeysAscending(
List<Function<? super T, ? extends S>> keyExtractors)
if (keyExtractors.isEmpty())
return (a, b) -> 0;
else
Function<? super T, ? extends S> firstSortKey = keyExtractors.get(0);
List<Function<? super T, ? extends S>> restOfSortKeys =
keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
And here's a demo with the call:
List<Function<? super Data, ? extends Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
Comparator<Data> test = parseKeysAscending(extractors);
List<Data> data = new ArrayList<>(Arrays.asList(
new Data(1, "z"),
new Data(2, "b"),
new Data(1, "a")));
System.out.println(data); // [[1, 'z'], [2, 'b'], [1, 'a']]
data.sort(test);
System.out.println(data); // [[1, 'a'], [1, 'z'], [2, 'b']]
The only way to make the code compile without warnings was to declare the list of functions as List<Function<Data, Integer>>
. But this works only with getters that return Integer
. I'm assuming that you might want to compare any mix of Comparable
s, i.e. the code above works with the following Data
class:
public class Data
private final Integer a;
private final String b;
private Data(int a, String b)
this.a = a;
this.b = b;
public Integer getA()
return a;
public String getB()
return b;
@Override
public String toString()
return "[" + a + ", '" + b + "']";
Here's the demo.
1
I still don't understand where the problem was or is (it compiled with java-11 that I have and use), but as a side note I think that method can be shorten topublic static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors) return keyExtractors.stream() .collect(Collector.of( () -> (Comparator<T>) (x, y) -> 0, Comparator::thenComparing, Comparator::thenComparing));
â Eugene
2 hours ago
Two interesting observations for your code Federico: 1). This doesn't work for me if I remove the stream in the call 2). I am getting a "cannot resolve method" in the recursive call yet it still successfully runs.
â Billy the Kid
2 hours ago
This looks like what I am going for. I think this is also a great example for me to understand contravariance/covariance in Java through the wildcard type. This may be IDE specific, but using the method as written gives me an unchecked method exception. If I use the intermediate "extractor" class as before, this warning goes away. Also, doesn't this still mean our method is accepting some type "S extends Comparable<? super S>"? I don't think there should be any way to call this with data that has both a string and integer field without getting the unchecked warning.
â Billy the Kid
42 mins ago
@BillytheKid Honestly, I'm not getting that exception/compilation error, so I cannot tell. Usually, Eclipse compiler is known to have issues with type inference (i.e. covariance/contravariance, generic types. lambdas and method references all mixed together). As to the unchecked warning, I don't think there's a way to get rid of it.
â Federico Peralta Schaffner
16 mins ago
add a comment |Â
up vote
3
down vote
accepted
up vote
3
down vote
accepted
It works for me without the Extractor
class and also without calling map(e -> e)
in the stream pipeline. Actually, streaming the list of extractors isn't needed at all if you use the correct generic types.
As to why your code doesn't work, I'm not completely sure. Generics is a tough and flaky aspect of Java... All I did was adjusting the signature of the parseKeysAscending
method, so that it conforms to what Comparator.comparing
actually expects.
Here's the parseKeysAscending
method:
public static <T, S extends Comparable<? super S>> Comparator<T> parseKeysAscending(
List<Function<? super T, ? extends S>> keyExtractors)
if (keyExtractors.isEmpty())
return (a, b) -> 0;
else
Function<? super T, ? extends S> firstSortKey = keyExtractors.get(0);
List<Function<? super T, ? extends S>> restOfSortKeys =
keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
And here's a demo with the call:
List<Function<? super Data, ? extends Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
Comparator<Data> test = parseKeysAscending(extractors);
List<Data> data = new ArrayList<>(Arrays.asList(
new Data(1, "z"),
new Data(2, "b"),
new Data(1, "a")));
System.out.println(data); // [[1, 'z'], [2, 'b'], [1, 'a']]
data.sort(test);
System.out.println(data); // [[1, 'a'], [1, 'z'], [2, 'b']]
The only way to make the code compile without warnings was to declare the list of functions as List<Function<Data, Integer>>
. But this works only with getters that return Integer
. I'm assuming that you might want to compare any mix of Comparable
s, i.e. the code above works with the following Data
class:
public class Data
private final Integer a;
private final String b;
private Data(int a, String b)
this.a = a;
this.b = b;
public Integer getA()
return a;
public String getB()
return b;
@Override
public String toString()
return "[" + a + ", '" + b + "']";
Here's the demo.
It works for me without the Extractor
class and also without calling map(e -> e)
in the stream pipeline. Actually, streaming the list of extractors isn't needed at all if you use the correct generic types.
As to why your code doesn't work, I'm not completely sure. Generics is a tough and flaky aspect of Java... All I did was adjusting the signature of the parseKeysAscending
method, so that it conforms to what Comparator.comparing
actually expects.
Here's the parseKeysAscending
method:
public static <T, S extends Comparable<? super S>> Comparator<T> parseKeysAscending(
List<Function<? super T, ? extends S>> keyExtractors)
if (keyExtractors.isEmpty())
return (a, b) -> 0;
else
Function<? super T, ? extends S> firstSortKey = keyExtractors.get(0);
List<Function<? super T, ? extends S>> restOfSortKeys =
keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
And here's a demo with the call:
List<Function<? super Data, ? extends Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
Comparator<Data> test = parseKeysAscending(extractors);
List<Data> data = new ArrayList<>(Arrays.asList(
new Data(1, "z"),
new Data(2, "b"),
new Data(1, "a")));
System.out.println(data); // [[1, 'z'], [2, 'b'], [1, 'a']]
data.sort(test);
System.out.println(data); // [[1, 'a'], [1, 'z'], [2, 'b']]
The only way to make the code compile without warnings was to declare the list of functions as List<Function<Data, Integer>>
. But this works only with getters that return Integer
. I'm assuming that you might want to compare any mix of Comparable
s, i.e. the code above works with the following Data
class:
public class Data
private final Integer a;
private final String b;
private Data(int a, String b)
this.a = a;
this.b = b;
public Integer getA()
return a;
public String getB()
return b;
@Override
public String toString()
return "[" + a + ", '" + b + "']";
Here's the demo.
edited 1 hour ago
answered 2 hours ago
Federico Peralta Schaffner
19.3k32862
19.3k32862
1
I still don't understand where the problem was or is (it compiled with java-11 that I have and use), but as a side note I think that method can be shorten topublic static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors) return keyExtractors.stream() .collect(Collector.of( () -> (Comparator<T>) (x, y) -> 0, Comparator::thenComparing, Comparator::thenComparing));
â Eugene
2 hours ago
Two interesting observations for your code Federico: 1). This doesn't work for me if I remove the stream in the call 2). I am getting a "cannot resolve method" in the recursive call yet it still successfully runs.
â Billy the Kid
2 hours ago
This looks like what I am going for. I think this is also a great example for me to understand contravariance/covariance in Java through the wildcard type. This may be IDE specific, but using the method as written gives me an unchecked method exception. If I use the intermediate "extractor" class as before, this warning goes away. Also, doesn't this still mean our method is accepting some type "S extends Comparable<? super S>"? I don't think there should be any way to call this with data that has both a string and integer field without getting the unchecked warning.
â Billy the Kid
42 mins ago
@BillytheKid Honestly, I'm not getting that exception/compilation error, so I cannot tell. Usually, Eclipse compiler is known to have issues with type inference (i.e. covariance/contravariance, generic types. lambdas and method references all mixed together). As to the unchecked warning, I don't think there's a way to get rid of it.
â Federico Peralta Schaffner
16 mins ago
add a comment |Â
1
I still don't understand where the problem was or is (it compiled with java-11 that I have and use), but as a side note I think that method can be shorten topublic static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors) return keyExtractors.stream() .collect(Collector.of( () -> (Comparator<T>) (x, y) -> 0, Comparator::thenComparing, Comparator::thenComparing));
â Eugene
2 hours ago
Two interesting observations for your code Federico: 1). This doesn't work for me if I remove the stream in the call 2). I am getting a "cannot resolve method" in the recursive call yet it still successfully runs.
â Billy the Kid
2 hours ago
This looks like what I am going for. I think this is also a great example for me to understand contravariance/covariance in Java through the wildcard type. This may be IDE specific, but using the method as written gives me an unchecked method exception. If I use the intermediate "extractor" class as before, this warning goes away. Also, doesn't this still mean our method is accepting some type "S extends Comparable<? super S>"? I don't think there should be any way to call this with data that has both a string and integer field without getting the unchecked warning.
â Billy the Kid
42 mins ago
@BillytheKid Honestly, I'm not getting that exception/compilation error, so I cannot tell. Usually, Eclipse compiler is known to have issues with type inference (i.e. covariance/contravariance, generic types. lambdas and method references all mixed together). As to the unchecked warning, I don't think there's a way to get rid of it.
â Federico Peralta Schaffner
16 mins ago
1
1
I still don't understand where the problem was or is (it compiled with java-11 that I have and use), but as a side note I think that method can be shorten to
public static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors) return keyExtractors.stream() .collect(Collector.of( () -> (Comparator<T>) (x, y) -> 0, Comparator::thenComparing, Comparator::thenComparing));
â Eugene
2 hours ago
I still don't understand where the problem was or is (it compiled with java-11 that I have and use), but as a side note I think that method can be shorten to
public static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors) return keyExtractors.stream() .collect(Collector.of( () -> (Comparator<T>) (x, y) -> 0, Comparator::thenComparing, Comparator::thenComparing));
â Eugene
2 hours ago
Two interesting observations for your code Federico: 1). This doesn't work for me if I remove the stream in the call 2). I am getting a "cannot resolve method" in the recursive call yet it still successfully runs.
â Billy the Kid
2 hours ago
Two interesting observations for your code Federico: 1). This doesn't work for me if I remove the stream in the call 2). I am getting a "cannot resolve method" in the recursive call yet it still successfully runs.
â Billy the Kid
2 hours ago
This looks like what I am going for. I think this is also a great example for me to understand contravariance/covariance in Java through the wildcard type. This may be IDE specific, but using the method as written gives me an unchecked method exception. If I use the intermediate "extractor" class as before, this warning goes away. Also, doesn't this still mean our method is accepting some type "S extends Comparable<? super S>"? I don't think there should be any way to call this with data that has both a string and integer field without getting the unchecked warning.
â Billy the Kid
42 mins ago
This looks like what I am going for. I think this is also a great example for me to understand contravariance/covariance in Java through the wildcard type. This may be IDE specific, but using the method as written gives me an unchecked method exception. If I use the intermediate "extractor" class as before, this warning goes away. Also, doesn't this still mean our method is accepting some type "S extends Comparable<? super S>"? I don't think there should be any way to call this with data that has both a string and integer field without getting the unchecked warning.
â Billy the Kid
42 mins ago
@BillytheKid Honestly, I'm not getting that exception/compilation error, so I cannot tell. Usually, Eclipse compiler is known to have issues with type inference (i.e. covariance/contravariance, generic types. lambdas and method references all mixed together). As to the unchecked warning, I don't think there's a way to get rid of it.
â Federico Peralta Schaffner
16 mins ago
@BillytheKid Honestly, I'm not getting that exception/compilation error, so I cannot tell. Usually, Eclipse compiler is known to have issues with type inference (i.e. covariance/contravariance, generic types. lambdas and method references all mixed together). As to the unchecked warning, I don't think there's a way to get rid of it.
â Federico Peralta Schaffner
16 mins ago
add a comment |Â
up vote
3
down vote
After Federico corrected me (thank you!) this is a single method you could do it with:
public static <T, S extends Comparable<? super S>> Comparator<T> test(List<Function<T, S>> list)
return list.stream()
.reduce((x, y) -> 0,
Comparator::thenComparing,
Comparator::thenComparing);
And usage would be:
// I still don't know how to avoid this raw type here
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA); // getA returns an Integer
extractors.add(Data::getB); // getB returns a String
listOfSomeDatas.sort(test(extractors));
2
Very nice, this does make sense as the original recursive function follows a pretty standard fold-like pattern.
â Billy the Kid
19 mins ago
I still likelist.stream().map(Comparator::comparing).reduce(Comparator::thenComparing).orElse((Comparator<T>) (a, b) -> 0)
more than this, but +1 for keeping all this generics nightmare simple.
â Federico Peralta Schaffner
12 mins ago
add a comment |Â
up vote
3
down vote
After Federico corrected me (thank you!) this is a single method you could do it with:
public static <T, S extends Comparable<? super S>> Comparator<T> test(List<Function<T, S>> list)
return list.stream()
.reduce((x, y) -> 0,
Comparator::thenComparing,
Comparator::thenComparing);
And usage would be:
// I still don't know how to avoid this raw type here
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA); // getA returns an Integer
extractors.add(Data::getB); // getB returns a String
listOfSomeDatas.sort(test(extractors));
2
Very nice, this does make sense as the original recursive function follows a pretty standard fold-like pattern.
â Billy the Kid
19 mins ago
I still likelist.stream().map(Comparator::comparing).reduce(Comparator::thenComparing).orElse((Comparator<T>) (a, b) -> 0)
more than this, but +1 for keeping all this generics nightmare simple.
â Federico Peralta Schaffner
12 mins ago
add a comment |Â
up vote
3
down vote
up vote
3
down vote
After Federico corrected me (thank you!) this is a single method you could do it with:
public static <T, S extends Comparable<? super S>> Comparator<T> test(List<Function<T, S>> list)
return list.stream()
.reduce((x, y) -> 0,
Comparator::thenComparing,
Comparator::thenComparing);
And usage would be:
// I still don't know how to avoid this raw type here
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA); // getA returns an Integer
extractors.add(Data::getB); // getB returns a String
listOfSomeDatas.sort(test(extractors));
After Federico corrected me (thank you!) this is a single method you could do it with:
public static <T, S extends Comparable<? super S>> Comparator<T> test(List<Function<T, S>> list)
return list.stream()
.reduce((x, y) -> 0,
Comparator::thenComparing,
Comparator::thenComparing);
And usage would be:
// I still don't know how to avoid this raw type here
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA); // getA returns an Integer
extractors.add(Data::getB); // getB returns a String
listOfSomeDatas.sort(test(extractors));
answered 37 mins ago
Eugene
61.5k986143
61.5k986143
2
Very nice, this does make sense as the original recursive function follows a pretty standard fold-like pattern.
â Billy the Kid
19 mins ago
I still likelist.stream().map(Comparator::comparing).reduce(Comparator::thenComparing).orElse((Comparator<T>) (a, b) -> 0)
more than this, but +1 for keeping all this generics nightmare simple.
â Federico Peralta Schaffner
12 mins ago
add a comment |Â
2
Very nice, this does make sense as the original recursive function follows a pretty standard fold-like pattern.
â Billy the Kid
19 mins ago
I still likelist.stream().map(Comparator::comparing).reduce(Comparator::thenComparing).orElse((Comparator<T>) (a, b) -> 0)
more than this, but +1 for keeping all this generics nightmare simple.
â Federico Peralta Schaffner
12 mins ago
2
2
Very nice, this does make sense as the original recursive function follows a pretty standard fold-like pattern.
â Billy the Kid
19 mins ago
Very nice, this does make sense as the original recursive function follows a pretty standard fold-like pattern.
â Billy the Kid
19 mins ago
I still like
list.stream().map(Comparator::comparing).reduce(Comparator::thenComparing).orElse((Comparator<T>) (a, b) -> 0)
more than this, but +1 for keeping all this generics nightmare simple.â Federico Peralta Schaffner
12 mins ago
I still like
list.stream().map(Comparator::comparing).reduce(Comparator::thenComparing).orElse((Comparator<T>) (a, b) -> 0)
more than this, but +1 for keeping all this generics nightmare simple.â Federico Peralta Schaffner
12 mins ago
add a comment |Â
up vote
1
down vote
- If I don't define the
Extractor
class, this will not compile. I cannot directly haveFunction
s or some sort of functional interface.
No, you can. You can define any Function<X, Y>
by a lambda, or method reference, or anonymous class.
List<Function<Data, Integer>> extractors = List.of(Data::getA, Data::getB);
- If I remove the identity function mapping line
.map(e -> e)
, this will not type check.
It still will, but the result may not be suitable for the method. You can always define generic parameters explicitly to make sure that everything goes as you expect.
extractors.<Function<Data, Integer>>stream().collect(Collectors.toList())
But there is no need here:
Comparator<Data> test = parseKeysAscending(extractors);
add a comment |Â
up vote
1
down vote
- If I don't define the
Extractor
class, this will not compile. I cannot directly haveFunction
s or some sort of functional interface.
No, you can. You can define any Function<X, Y>
by a lambda, or method reference, or anonymous class.
List<Function<Data, Integer>> extractors = List.of(Data::getA, Data::getB);
- If I remove the identity function mapping line
.map(e -> e)
, this will not type check.
It still will, but the result may not be suitable for the method. You can always define generic parameters explicitly to make sure that everything goes as you expect.
extractors.<Function<Data, Integer>>stream().collect(Collectors.toList())
But there is no need here:
Comparator<Data> test = parseKeysAscending(extractors);
add a comment |Â
up vote
1
down vote
up vote
1
down vote
- If I don't define the
Extractor
class, this will not compile. I cannot directly haveFunction
s or some sort of functional interface.
No, you can. You can define any Function<X, Y>
by a lambda, or method reference, or anonymous class.
List<Function<Data, Integer>> extractors = List.of(Data::getA, Data::getB);
- If I remove the identity function mapping line
.map(e -> e)
, this will not type check.
It still will, but the result may not be suitable for the method. You can always define generic parameters explicitly to make sure that everything goes as you expect.
extractors.<Function<Data, Integer>>stream().collect(Collectors.toList())
But there is no need here:
Comparator<Data> test = parseKeysAscending(extractors);
- If I don't define the
Extractor
class, this will not compile. I cannot directly haveFunction
s or some sort of functional interface.
No, you can. You can define any Function<X, Y>
by a lambda, or method reference, or anonymous class.
List<Function<Data, Integer>> extractors = List.of(Data::getA, Data::getB);
- If I remove the identity function mapping line
.map(e -> e)
, this will not type check.
It still will, but the result may not be suitable for the method. You can always define generic parameters explicitly to make sure that everything goes as you expect.
extractors.<Function<Data, Integer>>stream().collect(Collectors.toList())
But there is no need here:
Comparator<Data> test = parseKeysAscending(extractors);
edited 12 mins ago
answered 20 mins ago
Andrew Tobilko
21.8k83876
21.8k83876
add a comment |Â
add a comment |Â
up vote
0
down vote
Regarding your original code in the question, you can remove Extractor
class and use a raw Comparable
:
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
@SuppressWarnings("unchecked")
Comparator<Data> test = parseKeysAscending(extractors);
P.S. I also don't know how to get rid of the raw type here...
add a comment |Â
up vote
0
down vote
Regarding your original code in the question, you can remove Extractor
class and use a raw Comparable
:
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
@SuppressWarnings("unchecked")
Comparator<Data> test = parseKeysAscending(extractors);
P.S. I also don't know how to get rid of the raw type here...
add a comment |Â
up vote
0
down vote
up vote
0
down vote
Regarding your original code in the question, you can remove Extractor
class and use a raw Comparable
:
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
@SuppressWarnings("unchecked")
Comparator<Data> test = parseKeysAscending(extractors);
P.S. I also don't know how to get rid of the raw type here...
Regarding your original code in the question, you can remove Extractor
class and use a raw Comparable
:
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
@SuppressWarnings("unchecked")
Comparator<Data> test = parseKeysAscending(extractors);
P.S. I also don't know how to get rid of the raw type here...
answered 18 secs ago
Oleksandr
7,07533366
7,07533366
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52596151%2fhow-does-this-compile%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
There does seem to be some wonky stuff going on here. Question worth asking to me, since my IDE also optimizes out the mapping but then immediately refuses to compile. What version of Java are you using?
â Makoto
3 hours ago
I am using jdk 1.8.0_181
â Billy the Kid
3 hours ago