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.

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. Commented Aug 1, 2015 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. Commented Aug 1, 2015 at 20:32
  • It would also help if you used more descriptive names than A1 and F1. What are you comparing? Commented Aug 1, 2015 at 20:46
  • @bcorso I have added more details. Commented Aug 1, 2015 at 22:25
  • 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... Commented Aug 2, 2015 at 8:11

3 Answers 3

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);
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

@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.
Ah, yes. But that's not the desired behaviour. AFAICS, the aim is to compare the F1 instances.
I've updated my answer. Basically, I'm saying he over-engineered this problem, comparing T or comparing F1<T> should not be that difficult.
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...)
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).
|
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);
    }
}

5 Comments

Except that it isn't S extends A1<T extends F1<S>>, it's S extends A1<? extends F1<S>>. So isn't the real problem that the OP is effectively trying to call A1<?>.compare()?
If I write class F1 < S extends A1< F1<S>> >, then I cannot have the subclass F2, as well as A2 for A1. E.g.: public interface A2 <T extends F2<?>> extends A1<T> {...} and public class F2 <S extends A2<F2<S>>> extends F1<S> {...}. The error for F2 then is Bound mismatch: The type S is not a valid substitute for the bounded parameter <S extends A1<F1<S>>> of the type F1<S>
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.
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<S>, and the second parameter will have type F1<S>. But A2.compare was supposed to accept only F2 types...
@OliverCharlesworth Essentially yes, ? extends F1<S> means an unknown subtype of F1<S> which is not a useful thing. Anyway <S extends A1<T extends F1<S>>> is made-up syntax to illustrate a point, you can't place a type variable T in that position.
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.

7 Comments

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.
What does this recursive definition mean? A1<A extends A1<A>> How to interpret it?
@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.
@ruslancho: In terms of the recursive generic, see 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<A2>; that's valid because A2 indeed extends A1<A2>).
@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<A2> is also valid.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.