Generic class as argument - java

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;
}

Related

Java :: Using generics to instantiate an "at runtime" Multi-Comparator

I have a MultiComparator class that allows one to combine multiple sorts. For example, the code below creates a sort for Contacts.
// This works
contactList.sort(
new MultiComparator<>(
new ContactComparator(ContactComparator.Sort.CONTACT_NAME),
new ContactComparator(ContactComparator.Sort.ID)
)
);
However, I am needed to create the sort order on the fly at runtime, and I am having trouble figuring out how to do this as the java compiler keeps giving me an error: "Cannot infer arguments (unable to resolve constructor)". For simplicity, I have omitted the logic that would create the "ordered" List of sort fields, but assume that the user could create the order. My issue becomes, how to create the MultiComparator given this ordered List. Any ideas?
List<ContactComparator> orderedContactComparators = new ArrayList(); // assume this was created above
contactList.sort(
// compiler error: "Cannot infer arguments (unable to resolve constructor)
new MultiComparator<>(orderedContactComparators)
);
For reference, the other classes are below...
ContactComparator Class
public class ContactComparator implements Comparator<Contact> {
private Sort currentSort;
private boolean sortAsc;
public enum Sort {
NAME, ADDRESS, ID
}
public ContactComparator() {
currentSort = Sort.NAME;
sortAsc = true;
}
public ContactComparator(Sort sort) {
currentSort = Objects.requireNonNullElse(sort, Sort.NAME);
sortAsc = true;
}
#Override
public int compare(Contact o1, Contact o2){
// Compare logic here
}
}
MultiComparator Class
public class MultiComparator<T> implements Comparator<T> {
private List<Comparator<? super T>> comparators;
public MultiComparator(List<Comparator<? super T>> comparators) {
this.comparators = comparators;
}
#SafeVarargs
public MultiComparator(Comparator<? super T>... comparators) {
this(Arrays.asList(comparators));
}
public int compare(T o1, T o2) {
for (Comparator<? super T> c : comparators) {
int result = c.compare(o1, o2);
if (result != 0) {
return result;
}
}
return ObjectUtils.compare(o1.hashCode(), o2.hashCode());
}
#SafeVarargs
public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
list.sort(new MultiComparator<T>(comparators));
}
}

How to deliver the class of a generic type to a method in Java?

I want to implement a class that instantiates generic types.
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public void doSomething(E Element) {
T set = setClass.newInstance();
set.add(element);
}
}
I tried instantiating the class like this:
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet<>(HashSet<Integer>.class);
However using .class on a generic type does not seem to be allowed. How would I correctly pass the required Class of a generic type to the constructor?
Not sure it is good to expose the inner set type (Hash versus other) in the parameterized type.
Actually due to type erasure you can't instantiate parameterised types directly, but you can pass in a factory,
package langGenerics;
import java.util.HashSet;
import java.util.Set;
public class UseGenerics {
public static void main(String[] args) {
SetFactory<Integer> setFactory = HashSet::new;
DisjointSet<Integer> disjointSet = new DisjointSet<>(setFactory);
disjointSet.doSomething( 123 );
}
}
interface SetFactory<T> { Set<T> get(); }
class DisjointSet<T> {
private SetFactory<T> setFactory;
public DisjointSet(SetFactory<T> setFactory) {
this.setFactory = setFactory;
}
public void doSomething(T item) {
Set<T> set = setFactory.get();
set.add(item);
}
}
If you really want to init your own set storage, then I suggest you to pass Supplier to your constructor:
public static class DisjointSet<T extends Set<E>, E> {
T set;
public DisjointSet(Supplier<T> supplier) {
set = supplier.get();
}
public void doSomething(E element) {
set.add(element);
}
}
Then use it:
DisjointSet<HashSet<Integer>, Integer> set = new DisjointSet<>(HashSet::new);
if this is what you wanted,
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public static void main(String[] args) {
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet(new HashSet<Integer>().getClass());
}
}

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());
}
}

Comparator of a generic class

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);
}
};
}
}

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