2017-12-19 241 views
7

Mam interfejs o nazwie Bar i ogólna klasa Foo parametryzowane w rodzaju, że jestBar:W klasie ogólnej języka Java, dodaj dodatkowe ogólne ograniczenia na poziomie konstruktora?

class Foo<B extends Bar> { } 

Moja klasa ma konstruktora ogólnego przeznaczenia, która pobiera Class i Stream:

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

Próbuję dodać specjalistycznego konstruktora, który wymaga, aby jego rzeczywisty parametr metody zarówno jest Barienum klasa taka, że ​​mogę zadzwonić do mojego konstruktora ogólnego przeznaczenia ze specjalnego konstruktora:

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()); 
    } 
} 
+0

Czy może oznaczać 'clazz.getEnumConstants()'? Klasa 'Class' nie ma metody' getEnumeratedValues ​​() '. –

+0

dlaczego nie tylko 2 dodatkowe konstruktory specjalne? –

+0

@JohnBollinger - tak - Wkręciłem nazwę metody w swoim pośpiechu, aby wygenerować problem. Naprawianie :) – 0xbe5077ed

Odpowiedz

6

Ogólnie rzecz biorąc, można napisać konstruktorów generycznych. Ostatnio mieliśmy a question about them, and how they might be useful. W ten sposób można zapewnić konstruktora, który przyjmuje jako argument Class reprezentujący klasę, która zarówno rozciąga się jakiś konkretny inną klasę implementującą interfejs 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) { 
     // ... 
    } 
} 

Ale to nie całkiem Cię tam, gdzie chcesz być , ponieważ granice argumentu typu konstruktora muszą być jawnymi typami. Zmienne typu, takie jak parametr typu klasy B, nie służą, a bez sposobu na połączenie T z B, specjalny konstruktor generyczny nie może wywołać generalnego konstruktora.

Ale może to zrobić za pomocą metody fabryki zamiast specjalnego konstruktora:

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())); 
    } 
} 
+0

Dzięki za szczegółowe wyjaśnienie. Miałem nadzieję, że istnieje sposób, aby to osiągnąć za pomocą generycznych konstruktorów, ale jak już mówisz, nie ma możliwości powiązania granic parametru konstruktora z parametrem typu klasy. Trochę frustrujące! Ale myślę, że alternatywą jest skomplikowanie składni, aby nieco uprościć mało znany przypadek użycia. – 0xbe5077ed

-4

Jest to możliwe za pomocą operatora &: <Something extends Bar & Enum<?>>

+1

Na stackoverflow można oznaczyć coś jako blok kodu, umieszczając go na własnej linii, a następnie wciskając go. Ponieważ twój kod tego nie robi, jest przetwarzany jako html i odrzucany jako nieprawidłowy html – Ferrybig

5

nie sądzę możesz to zrobić za pomocą konstruktora. Albo utworzyć podklasę:

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

lub fabryczną metodę:

public static <T extends Enum<T>&Bar> Foo<T> of(Class<T> clazz) { 
    return new Foo<>(clazz, Arrays.stream(clazz.getEnumConstants())); 
}