Comparator of a generic class - java

I have a generic class :
class Card<T extends Enum<T> & Rank> implements Comparable<Card<T>>
Rank is java interface.
I'm trying to make a Comparator instance in the Card class.
public final static Comparator<Card> comparator = new Comparator<Card>() { ... }
The previous line has a warning.
Card is a raw type. References to generic type Card should be
parameterized
How can I declare the generic type of the Card class ?

public class Card<T extends Enum<T> & Rank> {
private final T rank;
private final Suit suit;
public Card(T rank, Suit suit)
{
this.rank = rank;
this.suit = suit;
}
public final static <T extends Enum<T> & Rank> Comparator<Card<T>> byRank()
{
return new Comparator<Card<T>>() {
#Override
public int compare(Card<T> o1, Card<T> o2)
{
int r = compareRank(o1, o2);
return r != 0 ? r : compareSuit(o1,o2);
}
};
}
public final static <T extends Enum<T> & Rank> Comparator<Card<T>> bySuit()
{
return new Comparator<Card<T>>() {
#Override
public int compare(Card<T> o1, Card<T> o2)
{
int r = compareSuit(o1,o2);
return r != 0 ? r : compareRank(o1, o2);
}
};
}
}

Related

Generic interfaces and covariance

the code below works fine:
public class ICopyableTest {
private interface ICopyable<T extends ICopyable<? extends T>> {
void copyFrom(T original);
}
private interface IVal<T> extends ICopyable<IVal<? extends T>> {
T getV();
}
private static class Val<T> implements IVal<T> {
private T v;
public T getV() {
return v;
}
public Val(final T v) {
this.v = v;
}
#Override public void copyFrom(final IVal<? extends T> original) {
v = original.getV();
}
}
private static class StrVal extends Val<String> {
public StrVal(final String v) {
super(v);
}
}
public static void main(String[] args) {
Val<Object> o1 = new Val<>(new Object());
Val<String> o2 = new Val<>("qwe");
StrVal o3 = new StrVal("zxc");
o1.copyFrom(o2); // that's the point
o1.copyFrom(o3);
o2.copyFrom(o3);
o3.copyFrom(o2);
Val<Object> toObj = (Val<Object>) (Val<?>) o2; // [1]
}
}
Basically i have ICopyable interface with provides copying functionality, IVal which adds storage of value above, and two example classes which implement Val. The point of <? extends T> is to provide covariant argument to the CopyFrom method, so you could do o1.copyFrom(o2) etc.
So it all works fine i guess.
Now let's say i want to have another parameterized over ICopyable or IVal class:
private static class Bla<T extends ICopyable<T>> {
final T value1;
final T value2;
public Bla(final T value1, final T value2) {
this.value1 = value1;
this.value2 = value2;
}
void letsCopy() {
value1.copyFrom(value2);
value2.copyFrom(value1);
}
}
Now why can't i instantiate it with any of the following?
new Bla<StrVal>(o3, o3);
new Bla<Val<Object>>(o1, o1);
new Bla<Val<String>>(o2, o2);
To be honest i'm a bit lost here myself and that's why i'm exploring it. There's a separate very important question of why working with generics is SO draining, when even after 5+ years working with java i can't figure out those things without half an hour meditation - am i just dumb?
I just want to have a parameterized class which will allow me to work with IVal/ICopyable values, note that it should be parameterized class, not individual method, so you can store the instances of those values in fields, for example.
I would have done that this way
public class Test {
interface Copyable<T> {
void copyFrom(Copyable<T> v);
T getV();
}
interface Val<T> extends Copyable<T> {
// Do not know if this is usefull
}
abstract class AbstractVal<T> implements Val<T> {
T value;
public AbstractVal(T val) {
this.value = val;
}
}
class StrVal extends AbstractVal<String> {
public StrVal(String o3) {
super(o3);
}
#Override
public void copyFrom(Copyable<String> v) {
this.value = v.getV();
}
#Override
public String getV() {
return this.value;
}
}
class Bla<T extends AbstractVal<S>, S> {
final T value1;
final T value2;
public Bla( T value1, final T value2) {
this.value1 = value1;
this.value2 = value2;
}
void letsCopy() {
value1.copyFrom(value2);
}
}
void test() {
StrVal o1 = new StrVal("qwe");
StrVal o2 = new StrVal("qwe2");
StrVal o3 = new StrVal("zxc");
Bla tester = new Bla<StrVal, String>(o1, o2);
tester.letsCopy();
System.out.println(tester.value1.getV());
}
public static void main(String[] args) {
Test t = new Test();
t.test();
}
}
Maybe a little complicated for what it's doing but I think it's the idea..

How do you refer to nested types using generics in Java?

How do you create a generic class that refers to nested generic types?
I'm trying to create a Comparator class which can compare the inner types of B without wanting to expose what those types are. In the following example I get a compiler warning for raw casting my T inner nested values to Comparable:
public class SSCCE {
// Compare my A instances.
class AComparator<T extends B> implements Comparator<T> {
#Override
public int compare(final T o1, final T o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
class A extends B<Integer> {
#Override Integer getValue() { return 1; }
}
class A2 extends B<String> {
#Override String getValue() { return "Test String!"; }
}
abstract class B<T extends Comparable<T>> {
abstract T getValue();
}
public static void main(String[] args) {
SSCCE sscce = new SSCCE();
AComparator<A> comparator = sscce.new AComparator<>();
comparator.compare(sscce.new A(), sscce.new A());
}
}
Is it possible to represent the inner values using to safely allow casting?
Things I've tried:
Creating a wildcard comparable (uncompilable) :
class AComparator2<T extends B<? extends Comparable<?>>> implements Comparator<T> {
#Override
public int compare(final T o1, final T o2) {
Comparable<?> o1value = (Comparable) o1.getValue();
Comparable<?> o2value = (Comparable) o2.getValue();
return o1value.compareTo(o2value);
}
}
Declaring a secondary generic parameter type (U), which simply postpones the problem:
class AComparator3<T extends B<U>, U extends Comparable<U>> implements Comparator<T> {
#Override
public int compare(final T o1, final T o2) {
U o1value = o1.getValue();
U o2value = o2.getValue();
return o1value.compareTo(o2value);
}
}
...
AComparator3<A, Comparable<U>> comparator = sscce.new AComparator3();
This comparator isn't to compare two instances of the classes A, rather part of their contents.
The wildcard solution does not work
class AComparator2<T extends B<?>> {
public int compare(T o1, T o2)
because T is too loose here; we can't make sure two T's can compare to each other -- it's possible that o1 is a B<X1> and o2 is a B<X2>, and X1, X2 are two different types.
Your 3rd solution restricts T to a specific B<U>
class AComparator3<T extends B<U>, U extends Comparable<U>>
this works perfectly; except that the use site has to specify U, even though U is deducible from T.
AComparator3<A, Integer>
^^^^^^^ duh!
This is annoying. The same problem has been asked before from other use cases. No good answers.
Fortunately, in your case, U isn't needed anywhere on use site, therefore we could simply use a wildcard for it
AComparator3<A, ?> comparator = sscce.new AComparator3<>();
comparator.compare(sscce.new A(), sscce.new A());
In fact, the comparator is a Comparator<A>, which is probably all you need. Also we can create a convenience method to hide the ugliness of new. So you may do something like
Comparator<A> comparator = sscce.comparator();
Have you consider Java 8 solution?
Comparator<A> comparator = ((t1,t2)-> t1.getValue().compareTo(t1.getValue()));
comparator.compare(sscce.new A(), sscce.new A());
You may be interested in comparator which should compare types extending B but only if they hold same comparable type. Such comparator may look like
class AComparator<T extends Comparable<T>> implements Comparator<B<T>> {
#Override
public int compare(final B<T> o1, final B<T> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
and you can use it like
AComparator<Integer> comparator = sscce.new AComparator<>();
comparator.compare(sscce.new A(), sscce.new A());
comparator.compare(sscce.new A(), sscce.new A2());//compilation error
Another option is to have B implement Comparable directly, since you are using getValue() to do the compare. The below gets rid of the warning:
import java.util.Comparator;
public class SSCCE {
class A extends B<Integer> {
#Override Integer getValue() { return 1; }
}
class A2 extends B<String> {
#Override String getValue() { return "Test String!"; }
}
abstract class B<T extends Comparable<T>> implements Comparable<B<T>>{
abstract T getValue();
#Override
public int compareTo(B<T> other)
{
return getValue().compareTo(other.getValue());
}
}
public static void main(String[] args) {
SSCCE sscce = new SSCCE();
Comparator.naturalOrder().compare(sscce.new A(), sscce.new A());
}
}
There are a few things you have to change to achieve what you want, which I believe if just implement a Generic Comparator.
First, AComparator should look like:
// Compare my A instances.
class AComparator<T extends Comparable<T>> implements Comparator<T> {
#Override
public int compare(final T o1, final T o2) {
return o1.compareTo(o2);
}
}
You don't need your class B, since A and A2 will implement Comparable directly. Just delete it.
Your A and A2 classes:
class A implements Comparable<A> {
#Override public int compareTo(A other) {
// your compare logic here
// return negative if less than, 0 if equal, positive if greater than
}
}
class A2 implements Comparable<A2> {
#Override public int compareTo(A2 other) {
// your compare logic here
// return negative if less than, 0 if equal, positive if greater than
}
}
It is important that you read the documentation for Comparable, to understand what is expected from the returned value.
Does that makes sense?
PS: I didn't test those codes, they are just out of my head.
I guess this is what you want:
public class SSCCE {
static class BComparator<E extends Comparable<E>> implements Comparator<B<E>> {
#Override
public int compare(final B<E> o1, final B<E> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
static class A extends B<Integer> {
#Override Integer getValue() { return 1; }
}
static class A2 extends B<String> {
#Override String getValue() { return "Test String!"; }
}
static abstract class B<T extends Comparable<T>> {
abstract T getValue();
}
public static void main(String[] args) {
SSCCE sscce = new SSCCE();
BComparator<Integer> comparator = new BComparator<>();
comparator.compare(new A(), new A());
BComparator<String> comparator2 = new BComparator<>();
comparator2.compare(new A2(), new A2());
}
}
If you don't want your comparator to be able to compare instances of two different subclasses of B (like A2 extends B<String> and A3 extends B<String>), the following works:
public class SSCCE {
static class BComparator<E extends Comparable<E>, T extends B<E>> implements Comparator<T> {
#Override
public int compare(final T o1, final T o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
static class A extends B<Integer> {
#Override Integer getValue() { return 1; }
}
static class A2 extends B<String> {
#Override String getValue() { return "Test String!"; }
}
static abstract class B<T extends Comparable<T>> {
abstract T getValue();
}
public static void main(String[] args) {
SSCCE sscce = new SSCCE();
BComparator<Integer, A> comparator = new BComparator<>();
comparator.compare(new A(), new A());
BComparator<String, A2> comparator2 = new BComparator<>();
comparator2.compare(new A2(), new A2());
}
}

Abstract Class with enum Comparator: How to make it work?

I would like to get the following thing to work. My abstract class T extends java.util.Comparator<T> and should therefore allow me to use the compareTo method. The problem is that o1.t1.compareTo(o2.t1) receives The method compareTo(capture#2-of ?) is undefined for the type capture#1-of ?
Could someone please explain the problem on a basic level and tell me how to potentially fix it? Thanks a lot in advance.
public class TypeTypeComparator<T extends java.util.Comparator<T>> {
public T t1;
public T t2;
public TypeTypeComparator() {
this.t1 = null;
this.t2 = null;
}
public TypeTypeComparator(T t1, T t2) {
this.t1 = t1;
this.t2 = t2;
}
public static enum Comparator implements java.util.Comparator<TypeTypeComparator<?>> {
T1_SORT {
public int compare(TypeTypeComparator<?> o1, TypeTypeComparator<?> o2) {
return o1.t1.compareTo(o2.t1);
}},
T2_SORT {
public int compare(TypeTypeComparator<?> o1, TypeTypeComparator<?> o2) {
return o1.t2.compareTo(o2.t2);
}};
public static java.util.Comparator<TypeTypeComparator<?>> getComparator(final Comparator... options) {
return new java.util.Comparator<TypeTypeComparator<?>>() {
public int compare(TypeTypeComparator<?> o1, TypeTypeComparator<?> o2) {
for ( Comparator option : options ) {
int result = option.compare(o1, o2);
if ( result != 0 )
return result;
}
return 0;
}
};
}
}
}
There were few issues in your implementation, I have solved the issues for you :)
1st issue
public class TypeTypeComparator<T extendsjava.util.Comparator<T>> {
You need Comparable here instead of a Comparator here. So it becomes
public class TypeTypeComparator<T extendsComparable<T>> {
2nd issue
Recursive generics in Comparable forces you to use ? for generics
public static enum Comparator implementsjava.util.Comparator<TypeTypeComparator<?>>
or
public static enum Comparator implements java.util.Comparator<TypeTypeComparator<Comparable<?>>>
You will again need to put ? due to recursive generic declaration in Comparable, so instead I recommend you resolve the recursive generics of Comparable as follows:
interface Comp extends java.lang.Comparable<Comp> {}
Now you need to replace all ? with Comp and you are done.
Here is the complete implementation:
interface Comp extends Comparable<Comp> {}
public class TypeTypeComparator<T extends Comp> {
public T t1;
public T t2;
public TypeTypeComparator() {
this.t1 = null;
this.t2 = null;
}
public TypeTypeComparator(T t1, T t2) {
this.t1 = t1;
this.t2 = t2;
}
public static enum Comparator implements java.util.Comparator<TypeTypeComparator<Comp>> {
T1_SORT {
#Override
public int compare(TypeTypeComparator<Comp> o1,
TypeTypeComparator<Comp> o2) {
return o1.t1.compareTo(o2.t1);
}
},
T2_SORT {
#Override
public int compare(TypeTypeComparator<Comp> o1,
TypeTypeComparator<Comp> o2) {
return o1.t2.compareTo(o2.t2);
}
};
public static java.util.Comparator<TypeTypeComparator<Comp>> getComparator(final Comparator... options) {
return new java.util.Comparator<TypeTypeComparator<Comp>>() {
public int compare(TypeTypeComparator<Comp> o1, TypeTypeComparator<Comp> o2) {
for ( Comparator option : options ) {
int result = option.compare(o1, o2);
if ( result != 0 )
return result;
}
return 0;
}
};
}
}
}
I would replace Comparator<TypeTypeComparator<?>> with Comparator<TypeTypeComparator> The problem is that one <?> and another are not equivalent.
BTW It appear you really want to be using Java 8 which would make all of this trivial. Java 7 will be End Of Life'd in April.
In Java 8 you would need a class like you have i.e. all the code would disappear. You can use built in functions like
list.sort(comparing(Type::getField1)
.andThen(comparing(t -> getItem().getOtherField())
.andThen(comparing(Type::getField2).reversed()));

Generic class as argument

I have a generic class Card . Rank is interface
class Card<T extends Enum<T> & Rank>
I am trying to create two static comparators of Card.
public final static Comparator<Card<?>> comparatorByAttribute1 = new Comparator<Card<?>>() {
#Override
public int compare(Card<?> o1, Card<?> o2)
{
...
}
};
How can I define that the type of o1 should be the same with o2 ?
Why not just use the actual type in the type declaration?
public final static Comparator<Card<ActualType>> comparatorByAttribute1 =
new Comparator<Card<ActualType>>() {
#Override
public int compare(Card<ActualType> o1, Card<ActualType> o2) {
return 0;
}
};
With...
public enum ActualType implements Rank {...}
Alternatively, if you want to keep the generic type <T>, you will need to resort to using a generic static method, because there is no way of having generic attributes in Java:
public final static <T extends Enum<T> & Rank> Comparator<Card<T>>
comparatorByAttribute1() {
return new Comparator<Card<T>>() {
#Override
public int compare(Card<T> o1, Card<T> o2) {
return 0;
}
};
}
Or, you resort to unsafe casting:
#SuppressWarnings({ "rawtypes", "unchecked" })
public final static <T extends Enum<T> & Rank> Comparator<Card<T>>
comparatorByAttribute1() {
// Your attribute
return (Comparator) comparatorByAttribute1;
}

Using generics with collection of enum classes implementing same interface

I am trying to do reverse lookup on few enum classes implementing same Field interface by iterating through list of Classes using Guava's Maps.uniqueIndex:
Field valueOfSearchName = null;
for (final Class<? extends Enum<?>> clazz : ImmutableList.of(
EntityField.class,
AddressField.class,
PersonFunctionType.class)) {
valueOfSearchName = Fields.valueOfSearchName(clazz, term.field()); // error
if (valueOfSearchName != null) {
// do something...
break;
}
}
I don't want to repeat same code (for making index and doing lookup) in all enum classes, so I use helper static class Fields containing Fields.valueOfSearchName method:
public static <E extends Enum<E> & Field> Field valueOfSearchName(
final Class<E> clazz, final String searchName) {
// TODO: cache the index
final ImmutableMap<String, E> index = Maps.uniqueIndex(
EnumSet.allOf(clazz), GET_SEARCH_NAME_FUNCTION);
return index.get(searchName);
}
Unfortunately, Eclipse shows an error:
Bound mismatch:
The generic method valueOfSearchName(Class<E>, String) of type Fields is not
applicable for the arguments (Class<capture#1-of ? extends Enum<?>>, String).
The inferred type capture#1-of ? extends Enum<?> is not a valid substitute
for the bounded parameter <E extends Enum<E> & Field>
The problem is Class<? extends Enum<?>> clazz in for-each loop (not matching Field), but I don't know how to deal with this case (obviously I cannot add & Field to clazz).
Consider Class<? extends List<?>. Class<? extends List<?> has two wildcards whereas <E extends List<E>> Class<E> only has generic parameter. The former will admit Class<ArrayList<String>>. So without doing something extra special for enums, the types are not compatible.
How to fix? An extra layer of indirection!
public final class MetaEnum<E extends Enum<E>> {
private final E clazz;
public static <E extends Enum<E>> MetaEnum<E> of(E clazz) {
return clazz;
}
private MetaEnum(E clazz) {
this.clazz = clazz;
}
public E clazz() {
return clazz;
}
// ...
}
for (final MetaEnum<?> meta : ImmutableList.of(
MetaEnum.of(EntityField .class),
MetaEnum.of(AddressField .class),
MetaEnum.of(PersonFunctionType.class)
)) {
Field valueOfSearchName = Fields.valueOfSearchName(
meta.clazz(), term.field()
);
...
(Usual Stack Overflow dislaimer: Not so much as attempted to compile.)
Inspired by Tom Hawtin's answer I created wrapper class holding Classes, but only those with signature <E extends Enum<E> & Field>:
public final static class FieldEnumWrapper<E extends Enum<E> & Field> {
private final Class<E> clazz;
private final ImmutableMap<String, E> index;
public static <E extends Enum<E> & Field>
FieldEnumWrapper<E> of(final Class<E> clazz) {
return new FieldEnumWrapper<E>(clazz);
}
private FieldEnumWrapper(final Class<E> clazz) {
this.clazz = clazz;
this.index = Maps.uniqueIndex(
EnumSet.allOf(clazz), new Function<E, String>() {
#Override
public String apply(final E input) {
return input.searchName();
}
});
}
public Class<E> clazz() {
return clazz;
}
public Field valueOfSearchName(final String searchName) {
return index.get(searchName);
}
}
Now:
for (final FieldEnumWrapper<?> fieldEnum : ImmutableList.of(
FieldEnumWrapper.of(EntityField.class),
FieldEnumWrapper.of(AddressField.class),
FieldEnumWrapper.of(PersonFunctionType.class))) {
valueOfSearchName = fieldEnum.valueOfSearchName("POD_I_OS_PARTNER");
// ...
is type-safe and inappropriate usage of FieldEnumWrapper's static factory:
FieldEnumWrapper.of(NotEnumAndFieldClass.class)
generates compile error.
Moreover, valueOfSearchName is now method of FieldEnumWrapper what make more sense that helper class.
maybe something like this:
import java.util.*;
class N {
static int n;
}
interface HasField {
int getField();
}
enum Color implements HasField {
r, g, b;
public int getField() {
return field;
}
private int field = N.n++;
}
enum Day implements HasField {
m, t, w, th, f, sa, su;
public int getField() {
return field;
}
private int field = N.n++;
}
class Helper {
Helper(Set<HasField> set) {
for (HasField hasField : set)
if (hasField instanceof Enum) {
Enum<?> e = (Enum<?>) hasField;
for (Object o : e.getDeclaringClass().getEnumConstants()) {
map.put(((HasField) o).getField(), (Enum<?>) o);
}
} else
throw new RuntimeException(hasField + " is not an enum!");
}
final Map<Integer, Enum<?>> map = new TreeMap<Integer, Enum<?>>();
}
public class Main {
public static void main(String[] args) {
Set<HasField> set = new LinkedHashSet<HasField>();
set.add(Color.r);
set.add(Day.m);
Helper helper = new Helper(set);
for (int i = 0; i < N.n; i++)
System.out.println(i + " " + helper.map.get(i));
}
}

Categories

Resources