7

I have an interface called Bar and a generic class Foo parameterized on a type that is a Bar:

class Foo<B extends Bar> { }

My class has a general purpose constructor that takes a Class and a Stream:

class Foo<B extends Bar> {
    B[] bs;
    Foo(Class<B> clazz, Stream<B> stream) { // General ctor
        bs = someFunctionOf(clazz, stream);
    }
}

I'm trying to add a specialized constructor which is requires that its actual method parameter both is a Bar and an enum class such that I can call my general purpose constructor from the special constructor:

class Foo<B extends Bar> {
    B[] bs;
    Foo(Class<B> clazz, Stream<B> stream) { // General ctor
        bs = someFunctionOf(clazz, stream);
    }
    // FIX THIS ----+
    //              |
    //              ˅
    Foo(Class<Something> clazz) { // Special ctor
        // Can we make this work so Something is a Bar and an enum
        // and we can call the other constructor like this?
        this(clazz, Arrays.stream(clazz.getEnumConstants());
    }
}
0xbe5077ed
  • 4,415
  • 5
  • 30
  • 69

3 Answers3

6

Generally speaking, you can write generic constructors. We recently had a question about them, and how they might be useful. In this way, you could provide a constructor that takes as an argument a Class representing a class that both extends some specific other class and implements interface Bar:

class Foo<B extends Bar> {
    B[] bs;
    Foo(Class<B> clazz, Stream<B> stream) { // General ctor
        bs = someFunctionOf(clazz, stream);
    }

    private B[] someFunctionOf(Class<B> clazz, Stream<B> stream) {
        return null;
    }

    <T extends SomeClass & Bar> Foo(Class<T> clazz) {
        // ...
    }
}

But that does not quite get you where you want to be, because the bounds of the constructor's type argument T need to be explicit types. Type variables such as the class's type parameter B do not serve, and without a way to connect T to B, the special generic constructor cannot invoke the general constructor.

But you can do this with a factory method instead of a special constructor:

class Foo<B extends Bar> {
    B[] bs;
    Foo(Class<B> clazz, Stream<B> stream) { // General ctor
        bs = someFunctionOf(clazz, stream);
    }

    private B[] someFunctionOf(Class<B> clazz, Stream<B> stream) {
        return null;
    }

    static <T extends Enum<T> & Bar> Foo<T> createEnumFoo(Class<T> clazz) {
        return new Foo<>(clazz, Arrays.stream(clazz.getEnumConstants()));
    }
}
John Bollinger
  • 138,022
  • 8
  • 74
  • 138
  • Thanks for the detailed explanation. I had hoped there was a way to accomplish it with generic constructors but as you say there's no way to tie the bounds of the constructor's type parameter back to the class' type parameter. A bit frustrating! But I guess the alternative is to complicate the syntax in order to slightly simplify an obscure use case. – 0xbe5077ed Dec 19 '17 at 16:19
5

I don't think you can do it with constructor. Either create subclass:

class EnumFoo<B extends Enum<B>&Bar> extends Foo<B> {
    public EnumFoo(Class<B> clazz) {
        super(clazz, Arrays.stream(clazz.getEnumConstants()));
    }
}

or factory method:

public static <T extends Enum<T>&Bar> Foo<T> of(Class<T> clazz) { 
    return new Foo<>(clazz, Arrays.stream(clazz.getEnumConstants()));
}
user158037
  • 2,582
  • 1
  • 22
  • 26
-4

This is possible by using the & operator: <Something extends Bar & Enum<?>>

rene
  • 39,748
  • 78
  • 111
  • 142
user1383093
  • 99
  • 1
  • 2
  • 12
  • 1
    On stackoverflow, you can mark something as a code block by putting it on its own line, and then indenting it. Since your code doesn't do this, it is parsed as html and rejected as invalid html – Ferrybig Dec 19 '17 at 15:39