2012-05-02 8 views
15

Próbuję określić, że klasa generyczna musi być tablicą, a jeszcze lepiej prymitywną tablicą. Do tej pory jest to, co mam pracy:Typy generyczne i tablice Java, nie to, co myślisz (na przykład tablice generyczne)

interface Foo<T> { 
    void process(T data); 
} 

public class Moo implements Foo<int[]> { 
    void process(int[] data) { 
    // do stuff here 
    } 
} 

To wszystko jest całkowicie poprawny kod Java i działa, ponieważ prymitywne tablice przedłużyć Object. Zauważ, że to pytanie jest zupełnie inne niż ze wszystkich innych pytań generycznych tablicy Javy, które ciągle znajduję. W tej sytuacji ludzie chcą stworzyć tablicę z rodzaju ogólnego.

Problem polega na tym, że typem T może być wszystko, co rozszerza obiekt. Co chcę zrobić coś takiego:

<T extends ONLY ARRAYS> 

lub

<T extends ONLY PRIMITIVE ARRAYS>. 

Czy to możliwe?

EDYCJA: Ostatecznym celem byłoby dodanie sprawdzenia czasu kompilacji dla przekazywanego typu tablicy. W tej chwili każdy stary obiekt może zostać przekazany, a skompilowany. Błąd zostanie wykryty tylko w środowisku wykonawczym, gdy zgłoszony zostanie wyjątek rzutu klasowego. W rzeczywistości jest to cały punkt Generics w Javie, aby dodać silniejsze sprawdzanie czasu kompilacji.

+2

dlaczego trzeba to zrobić? – Jeffrey

+0

Zgadzam się, że "powinno" być możliwe. Moje przeczucie mówi mi, że nie ma innego rodzaju nadstrukturalnego z klas prymitywnych niż Obiekt. (Istnieje klasa isArray() w klasie Class) – esej

+0

Pojedynczy interfejs do przetwarzania obrazów z danymi różnych typów pierwotnych. Alternatywą jest posiadanie innego interfejsu dla każdego typu, który jest gorszym kludge. Przykład: Foo_S32, Foo_U8, Foo_F32, ... itd. –

Odpowiedz

10

Nie możesz tego zrobić. Żadna klasa nie może rozszerzyć tablicy, więc nigdy nie będzie typu, który spełnia ogólny parametr T extends Object[]. (. Inne niż Object[] się, ale wtedy nie będzie za pomocą rodzajowych)

Co można zrobić, to coś takiego:

public interface Foo<T extends Number> { 
    public void process(T[] data); 
} 

Ale wtedy może napotkasz problemy z wydajnością z boksu.

+4

Nie całkiem w porządku, tablice określonego typu, takie jak String [], można przypisać do Object []. Ale to nie pomaga w prymitywnych tablicach. –

+1

@StevenSchlansker Podany przeze mnie przykład był moot i tak, tablice nie mogą być używane jako parametr związany. – Jeffrey

+0

Masz rację, że alternatywne podejście może zabić wydajność. Po zrobieniu nieco więcej wyszukiwania nie mogę znaleźć żadnego wyjątku specjalnego języka, który pozwoliłby na taki rodzaj ograniczenia, jaki chcę. Wybór tego jako rozwiązania, ponieważ jako pierwszy podałeś poprawną odpowiedź. –

1

Nie można zrobić. Nie „interesujący” interfejs ani supertypem ale obiekt istnieje dla tablic prymitywów:

public class SOTEST {  
    public static void main(String[] args) throws Exception { 
     int[] arr = new int[] {}; 
     Class c = arr.getClass(); 
     for(Class clazz:c.getInterfaces()) { 
      System.out.println(clazz.getName()); 
     } 

     System.out.println(c.getSuperclass().toString()); 
    } 
} 
8

W Javie, parametry typu mogą only być ograniczane przez stosunku podtypu, a jedyne wspólne supertypes wszystkich tablic areObject, Clonable i Serializable. Najbliżej można dostać to, aby ograniczyć do Object[], który jest supertypem wszystkich tablic z non-prymitywnych typów komponentów, ewentualnie do Number[], który jest supertypem z Integer[], Long[] ...

Nawet jeśli zrobił Java wspierać takie ograniczenie, w jaki sposób zrobiłbyś coś użytecznego z tą tablicą? Nie można było odczytywać poszczególnych elementów, ponieważ nie można zadeklarować zmiennej przechowującej wyniki, ani pisać poszczególnych elementów, ponieważ nie można zapisać wyrażenia przypisywanego do elementu tablicy.

To powiedziawszy, chciałbym związać zmienną typu do typu składnika, a nie rodzaj tablicy:

interface Foo<T extends Whatever> { 
    void process(T[] data); 
} 

bo można odnieść do T[] wiedząc T, ale znając T extends Object[] bezpośrednio nie pozwalają na odnoszą się do typu komponentu.

Edit: Jeffrey słusznie wskazuje, że typ tablicy nie mogą być wykorzystywane w granicach typu, tj <T extends Whatever[]> nie kompiluje, więc na pewno trzeba śledzić moje porady deklarowania <T extends Whatever>T[] i używać w odniesieniu do rodzaju matrycy.

+0

Punkt, w którym chce się dodać ograniczenie, nie jest taki, aby można było napisać uogólnioną klasę manipulującą tablicą, ale aby interfejs API był łatwiejszy w obsłudze. W tej chwili nie ma kontroli czasu kompilacji, a błędy będą przechwytywane tylko w czasie wykonywania. –

-2

Tablice X nie mają hierarchii typów. Integer [] nie jest podklasą Number [].

Aby dostać to, czego chcesz, użyj typ komponentu tablicy jako parametr typu T i zadeklarować parametry i powrócić jako typy tablic T.

+0

-1 dla niepoprawnych informacji: 'Integer []' * jest * podtypem 'Number []'. Na przykład: "Number [] numbers = new Integer [] {1, 2, 3};' kompiluje się dobrze ... – meriton

+0

Cóż - to orzechy, a to jest dziura w systemie typu Java. Ten \t Integer [] ints = new Integer [] {1, 2, 3}; \t Liczba [] numbers = ints; \t numbers [1] = new Float (4); \t dla (int x: ints) { \t \t System.out.println (x); \t} kompiluje i generuje wyjątek ArrayStoreException w czasie wykonywania. – PaulMurrayCbr

+1

Tak, kowariancja tablic narusza substytucyjność liskov (chyba że traktujemy ArrayStoreExceptions jako część umowy tablic, tak jak robi to JLS). Jednak sprawdzanie w czasie wykonywania gwarantuje, że środowisko Java pozostanie bezpieczne w środowisku wykonawczym. Jeśli chodzi o "orzechy", z pewnością masz prawo do swojej opinii, ale myślę, że skupienie się tylko na luce w systemie typu jest raczej ograniczonym poglądem. Tablice są kowariantne i są sprawdzane w czasie wykonywania. Generics muszą być kowariantne w każdej witrynie użytkowania i nie są sprawdzane w czasie wykonywania (faktycznie umożliwiając uszkodzenie sterty). Który jest lepszy projekt? – meriton