1

I would like to have an inheritance of generic classes and interfaces. There is one issue I cannot understand. The problem is reflected in the following simplified code:

public interface A1 <T extends F1<?>> {
    public void compare(T f1, T f2);
}

public class F1 < T extends A1<? extends F1<T>> >
{
    T a;
    public void compare(F1<T> f) {
        a.compare(this, f);
    }
}

A1 is in A1.java, F1 is in F1.java.

I get the compile-time error:

The method compare(capture#1-of ? extends F1<T>, capture#1-of ? extends F1<T>) 
in the type A1<capture#1-of ? extends F1<T>> is not applicable for the 
arguments (F1<T>, F1<T>)

I need A1 < ? extends F1< T>> for classes subclassing F1.

Additional information (after editing):

I would like to have 2 hierarchies ("O" instead of "F", "C" instead of "A"):

  • The first hierarchy of classes (O1, O2 ...) contains all relevant information of the object as well as the "comparator" (any function which analyses a pair of 2 objects of the same type). The class of the object defines the type of the comparator which it accepts, and the main requirement is that the comparator should accept 2 object of the type of the hosting object. For example, O1 can only accept all the comparators which compare O1's, O2 can have comparators that accept O2's.
  • The second hierarchy of classes is comparators (C1, C2...) themselves. they compare objects from the hierarchy 1.

Each element in both hierarchies adds supplementary functionality which is leveraged in subclasses.

Let's consider the following use case, one of the paths through these hierarchies:

  • O1 has 2 members (not types!): val (any type), cmp (of type C1 which only accepts O1).
  • C1_1 does not have a corresponding object O, but it implements additional functionality related to collections. It can compare objects ("basic" types like int, String, TColor and "complex" objects O's) in the collection and apply the aggregator which returns only 1 value (e.g. maximum). If the collection is of basic types, cmp2 is utilized to compare elements within the collection. For complex objects in collections, their integrated comparators are utilized.
  • C1_1_1 implements how to compare lists of objects (1:1). C1_1_2 (not presented) can be used to compare sets of objects, e.g. with the cross product.
  • O1_1 has val of the type TVALB which extend List<TVALA> and the comparator C1_1_1.

Other subclasses of C1_1_1/2 can have more refined implementations for comparing basic and complex objects.

The supporting code:

public abstract class O1 <TVAL, TC extends C1<? extends O1<TVAL>>>
{
    TVAL val;
    TC cmp;
    public double compare(O1<TVAL, TC> o) {
        return cmp.compare(this, o);
    }
}
public interface C1 <TO extends O1<?, ?> > {
    public double compare(TO t1, TO t2) {
      // ...
    }
}

public abstract class C1_1 <
        TVALA,
        TVALB extends Collection<TVALA>,
        TO extends O1<TVALB, ?>
    >
    extends C1 <TO> {

    Cmp2<TVALA, Double> cmp2;
    IAggregator<TVALA> aggr;

    Double compare(TO o1, TO o2) {
        return aggr.apply(
            compPairs(o1.val, o2.val, cmp2)
        );
    }

    abstract pairs<TVALA> compPairs(
        TVALB b1
        , TVALB b2
        , Cmp2<TVALA> cmp2);

    // aggregation
    public static class Aggr1<TVALA> implements IAggregator<TVALA> {
        public Double apply(pairs<TVALA> pairs) {
            //...
        }
    }
    public static class Aggr2<TVALA> implements IAggregator<TVALA> {
        // ...
    }
}

public abstract class C1_1_1 <
        TVALA,
        TVALB extends List<TVALA>,
        TO extends O1<TVALB, ?>
    >
    extends C1_1 <TO> {
    pairs<TVALA> compPairs(
            TVALB b1
            , TVALB b2
            , Cmp2<TVALA> cmp2) {
        // ...
    }
}

public abstract class O1_1 <
    TVALA extends O1<?,?>,
    TVALB extends List<TVALA>
    , TC extends C1_1_1<TVALA, TVALB, ? extends O1_1<TVALA, TVALB, TC>>>
    extends O1<TVAL>
{
    // ...
}

I use dependency injection to assign comparators (C-tree to objects O) in the specific class. Therefore, this approach (if it is feasible) can give the possibility to have a flexibility in choosing the proper comparator for specific types of objects.

ruslancho
  • 47
  • 7
  • To clarify, let's assume that F1 is a generic type of a specific "object", A1 is a generic type of a specific comparator which is used to compare 2 specific objects of the same type. – ruslancho Aug 01 '15 at 19:34
  • You should probably give a little more detail of what you are trying to do given that there have been 3 answers in the last hour -- apparently, none of which you have found useful. – bcorso Aug 01 '15 at 20:32
  • It would also help if you used more descriptive names than `A1` and `F1`. What are you comparing? – Sotirios Delimanolis Aug 01 '15 at 20:46
  • @bcorso I have added more details. – ruslancho Aug 01 '15 at 22:25
  • @SotiriosDelimanolis A1,2,3... (or O1,2,3...) represent different features of an object, size, names, prise, etc. F1,2,3 (or C1,2,3...) are "functions" which can be used to compare objects by their features. – ruslancho Aug 01 '15 at 22:28
  • **Result**. The only solution I was able to find thanks to your helpful suggestions is to merge these 2 trees. It does not have the flexibility I wanted to have, but it fits OK to the task I have. The problem with these 2 trees is the challenge to have "recursive" generic types, in which classes from one tree refer to classes in another tree which in turn refer to the first tree (in symbolic form, `O1>` and `A1>`). This adds a huge complexity when dealing with subclasses. – ruslancho Aug 02 '15 at 06:59
  • 1
    Where did my answer fall short? (Other than the polymorphic thing, which is inherently unavoidable.). And note that the generics are not mutually recursive, so it's unclear what the remaining complexity is... – Oliver Charlesworth Aug 02 '15 at 08:11

3 Answers3

1

Let's first rename the type variables because the two T's stand for different things, and hopefully it makes things clearer:

public interface A1 <T extends F1<?>> {
    public void compare(T t1, T t2);
}

public class F1 < S extends A1<T extends F1<S>> >
{
    S s;
    public void compare(F1<S> f) {
        s.compare(this, f);
    }
}

Now you see that you are actually calling compare on s, which is an instance of A1<T>, where T can be a subclass of F1<S>. The parameters you are passing however have type F1<S> and not the subclass T, which is pretty much what the compiler is saying.

Essentially you made the method parameters covariant, when maybe they should be contravariant. For that, use super instead of extends:

public class F1 < S extends A1<? super F1<S>> >
{
    S s;
    public void compare(F1<S> f) {
        s.compare(this, f);
    }
}
Joni
  • 108,737
  • 14
  • 143
  • 193
  • Except that it isn't `S extends A1>`, it's `S extends A1 extends F1>`. So isn't the real problem that the OP is effectively trying to call `A1>.compare()`? – Oliver Charlesworth Aug 01 '15 at 19:49
  • If I write class F1 < S extends A1< F1> >, then I cannot have the subclass F2, as well as A2 for A1. E.g.: `public interface A2 > extends A1 {...}` and `public class F2 >> extends F1 {...}`. The error for F2 then is `Bound mismatch: The type S is not a valid substitute for the bounded parameter >> of the type F1` – ruslancho Aug 01 '15 at 19:52
  • `super` in F1 won't work for me. I would like to have an inheritance for F1 and A1 as mentioned in my previous comment. – ruslancho Aug 01 '15 at 20:17
  • You won't be able to have that. Think about it in terms of the types being passed to `A2.compare` in the inherited `F2.compare` method if it was possible. The first parameter will have type `F2`, and the second parameter will have type `F1`. But `A2.compare` was supposed to accept only F2 types... – Joni Aug 01 '15 at 20:28
  • @OliverCharlesworth Essentially yes, `? extends F1` means an unknown subtype of `F1` which is not a useful thing. Anyway `>>` is made-up syntax to illustrate a point, you can't place a type variable T in that position. – Joni Aug 01 '15 at 20:47
1

The problem is that a is of type A1<? extends F1<T>>, i.e. parameterised by a wildcard. You cannot then call A1<?>.compare, because the compiler doesn't know what specific type ? stands for.

You'd see the same behaviour for this much simpler code:

A1<? extends Object> a = ...;
a.compare(new Object(), new Object());

If I understand your use-case correctly, you just want to be able to parameterise F1 on specific comparators. So it seems like you should just be able to do this:

interface A1<A extends A1<A>> {
    public void compare(F1<A> f1, F1<A> f2);
}

class F1<A extends A1<A>> implements Comparable<F1<A>> {
    A a;
    @Override
    public void compareTo(F1<A> f) {
        a.compare(this, f);
    }
}

Note that I've made F1 implement Comparable, because that's good practice.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • It seems it is a good solution. Thank you! The only thing, the `compareTo` function accepts all different objects which are subclass of F1. But it is not that big issue for me. – ruslancho Aug 01 '15 at 23:04
  • What does this recursive definition mean? `A1>` How to interpret it? – ruslancho Aug 01 '15 at 23:05
  • @ruslancho: There's not much you can do about that. The nature of polymorphism means that an instance of a subclass can always be substituted when a superclass is expected. – Oliver Charlesworth Aug 01 '15 at 23:11
  • 1
    @ruslancho: In terms of the recursive generic, see http://stackoverflow.com/questions/211143/java-enum-definition. The basic idea is that the only things that satisfy the bound are things that inherit from it (so imagine you did `class A2 implements A1`; that's valid because `A2` indeed extends `A1`). – Oliver Charlesworth Aug 01 '15 at 23:13
  • @ruslancho: the only reason it's there is because the bound on `A` in `F1` requires it – newacct Aug 05 '15 at 03:57
  • 1
    @OliverCharlesworth: "The basic idea is that the only things that satisfy the bound are things that inherit from it" Note that `class A3 implements A1` is also valid. – newacct Aug 05 '15 at 04:00
  • @newacct: Indeed, I don't think there's much one can do about that. But luckily A3 won't then be compatible with the F1 bound. – Oliver Charlesworth Aug 05 '15 at 05:48
1

You can remove your A1 class and just use Comparable interface.

public class F1<T extends Comparable<T>> implements Comparable<F1<T>> {
    T t;

    @Override public int compareTo(F1<T> other){
        return t.compareTo(other.t);
    }
}
bcorso
  • 45,608
  • 10
  • 63
  • 75
  • @OliverCharlesworth that's why I wrote `a.compareTo(f.a)`, where `f.a` is a `T`. In the end he is trying to compare `T`'s, I think he just over-engineered it a bit. – bcorso Aug 01 '15 at 20:14
  • Ah, yes. But that's not the desired behaviour. AFAICS, the aim is to compare the `F1` instances. – Oliver Charlesworth Aug 01 '15 at 20:15
  • I've updated my answer. Basically, I'm saying he over-engineered this problem, comparing T or comparing F1 should not be that difficult. – bcorso Aug 01 '15 at 20:19
  • I think he wants to be able to inject specific comparison behaviour, though. If I understand the use-case correctly, then I think the code in my answer does the job (although `F1` should probably implement `Comparable`...) – Oliver Charlesworth Aug 01 '15 at 20:20
  • This does not reflect the idea to have 2 trees of hierarchies. The first tree of generic types is objects F1 (generic), F2 (generic, subclass of F1), etc., the second tree is comparators: A1 (generic, compares F1's), A2 (generic, subclass of A1, compares F2's). – ruslancho Aug 01 '15 at 20:21
  • @ruslancho can you take that line of thought and add it to your question. It would probably help a lot if you gave an example of how you expect to use the classes, i.e. how would you actually instantiate them as you just described in words. – bcorso Aug 01 '15 at 20:26