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

Community
  • 1
  • 1
Gelin Luo
  • 14,035
  • 27
  • 86
  • 139
  • My answer [here](http://stackoverflow.com/questions/7354740/is-there-a-way-to-refer-to-the-current-type-with-a-type-variable/7355094#7355094) seems related. – Paul Bellora Oct 17 '13 at 20:44
  • @PaulBellora That is really a comprehensive answer. Nice. I guess that is what OP needs here. – Rohit Jain Oct 17 '13 at 20:48
  • Yes @PaulBellora, I read that before. But my case is about a container inheritance family, which introduce another `` type. Can you come up with a solution based on my code? – Gelin Luo Oct 17 '13 at 20:48
  • 1
    Sure, I'll have a look later today if no one else takes up the challenge. – Paul Bellora Oct 17 '13 at 20:51
  • I've deleted my answer here and posted [a new one](http://stackoverflow.com/a/19471914/697449) to the original question. – Paul Bellora Oct 19 '13 at 22:42

1 Answers1

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.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • I know it is illegal, while what I need is how to resolve or work around it. Come to the case, `Bar` is a type of `Foo` so I suppose the root `BaseFoo>` could be replaced as `BaseFoo`, which is the same as another inheritance route via `BaseBar`, isn't it? – Gelin Luo Oct 17 '13 at 20:30
  • @green No it's not like that. A `List` is not a `List`. The convariant relationship between types don't follow while using generics. – Rohit Jain Oct 17 '13 at 20:32
  • Thanks Rohit. Anyway to work achieve what I want, ie. a fluent API across a container hierarchy? – Gelin Luo Oct 17 '13 at 20:35
  • @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. – Rohit Jain Oct 17 '13 at 20:37
  • 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. – Gelin Luo Oct 17 '13 at 20:44