BaseFoo cannot be inherited with different arguments: <T,X.Bar<T>> and <T,X.Foo<T>>
Asked Answered
P

1

5

This is a simplified version of Java inherited Fluent method return type in multiple level hierarchies.

Given the following code:

public enum X {
    ;
    static interface BaseFoo<T, S extends BaseFoo<T, S>> {
        S foo();
    }

    static interface Foo<T> extends BaseFoo<T, Foo<T>> {
        void foo1();
    }

    static interface BaseBar<T, S extends BaseBar<T, S>> extends BaseFoo<T, S> {
        S bar();
    }

    static interface Bar<T> extends BaseBar<T, Bar<T>>, Foo<T> {
        void bar1();
    }

}

run javac X.java I get the error message:

X.java:15: error: BaseFoo cannot be inherited with different arguments: <T,X.Bar<T>> and <T,X.Foo<T>>
    static interface Bar<T> extends BaseBar<T, Bar<T>>, Foo<T> {
           ^

Anyone has any solution?

Disclaim: I am trying to use the pattern to implement the fluent interface across a container class inheritance hierarchy.

Background: to make it easier for people to understand why I need this, here is the story. I want to create a container family: Traversal <- Sequence <- List. So Traversal has a method Traveral<T> accept(Visitor<T>) (no PECS for short), this method should always return this after iterating the visitor through the elements. When I have a List type, I want the method return List<T> instead of Traversal<T> because I want to make it possible to call something like myList.accept(v).head(15), where head(int) is a method of List not Traversal

Pierre answered 17/10, 2013 at 20:17 Comment(5)
My answer here seems related.Sheply
@PaulBellora That is really a comprehensive answer. Nice. I guess that is what OP needs here.Dall
Yes @PaulBellora, I read that before. But my case is about a container inheritance family, which introduce another <T> type. Can you come up with a solution based on my code?Pierre
Sure, I'll have a look later today if no one else takes up the challenge.Sheply
I've deleted my answer here and posted a new one to the original question.Sheply
D
12

A class or interface cannot implement or extend from different instantiation of a generic interface. Your Bar interface is breaking this rule. Let's examine the interface declaration:

static interface Bar<T> extends BaseBar<T, Bar<T>>, Foo<T>

So, Bar<T> extends two interfaces:

  • BaseBar<T, Bar<T>>
  • Foo<T>

In addition to that, those two interfaces extend from different instantiation of the same interface BaseFoo.

  • BaseBar<T, S extends BaseBar<T, S>> extends BaseFoo<T, S>
  • Foo<T> extends BaseFoo<T, Foo<T>>

Those inherited interfaces are eventually also the super interfaces of Bar interface. Thus your Bar interface tries to extend from 2 different instantiation of BaseFoo, which is illegal. Let's understand the reason using a simple example:

// Suppose this was allowed
class Demo implements Comparable<Demo> , Comparable<String> {
    public int compareTo(Demo arg)     { ... } 
    public int compareTo(String arg) { ... } 
}

then after type erasure, compiler would generate 2 bridge methods, for both the generic method. The class is translated to:

class Demo implements Comparable<Demo> , Comparable<String> {
    public int compareTo(Demo arg)     { ... } 
    public int compareTo(String arg) { ... } 

    // Bridge method added by compiler
    public int compareTo(Object arg)     { ... } 
    public int compareTo(Object arg) { ... } 
}

So, that results in creation of duplicates bridge method in the class. That is why it is not allowed.

Dall answered 17/10, 2013 at 20:24 Comment(5)
I know it is illegal, while what I need is how to resolve or work around it. Come to the case, Bar<T> is a type of Foo<T> so I suppose the root BaseFoo<T, Foo<T>> could be replaced as BaseFoo<T, Bar<T>, which is the same as another inheritance route via BaseBar, isn't it?Pierre
@green No it's not like that. A List<Number> is not a List<Integer>. The convariant relationship between types don't follow while using generics.Dall
Thanks Rohit. Anyway to work achieve what I want, ie. a fluent API across a container hierarchy?Pierre
@green I would have to look into what a Fluent API is. Or you can explain in brief what you are trying to do. May be with some more code.Dall
So basically I want create a container family of Traversal <- Sequence <- List, while there are some method defined to return this to facilitate fluent method call, like myTrv.map(myMapFunc).filter(myFilterFunc), and when your instance is of List type, then your map() and filter() should return a List, instead of Traversal, so that you can write code like myLst.map(mapFunc).head(5).append(anotherLst), where head(n) and append(...) are List method but not Traversal method. Java8's BaseStream and Stream class use this pattern, but only in one level.Pierre

© 2022 - 2024 — McMap. All rights reserved.