2017-03-24 24 views
11

ja chce osiągnąć coś takiego w Javie, ale coraz skompilować błąd czasu:Rozwiąż Java Generic typ błędu z klasy abstrakcyjnej metody

The method onMessage(TextMessage, Class) in the type AbstractTestLoader is not applicable for the arguments (TextMessage, Class)

rozumiem przyczynę tego błędu, ale czuję też nie powinno być sposób na osiągnięcie tego przy rzucaniu lub może być jakiś inny sposób.

public abstract class AbstractTestLoader<T extends AbstractEntity<T>> { 

    public void onMessage(TextMessage message) throws Exception { 
     onMessage(message, this.getClass()); // I want to correct this line in such a way so that I can call below method with actual Class of {T extends AbstractEntity<T>} 
    } 

    public void onMessage(TextMessage message, Class<T> clazz) throws Exception { 
     //here my original logic will go 
    } 
} 
+0

Dlaczego * czujesz się * tak? Być może chcesz dodać swoje zrozumienie podstawowego problemu, aby upewnić się, że wszyscy są na tej samej stronie. Ponieważ musimy zgodzić się z podstawową przyczyną podczas badania przestrzeni rozwiązania. – GhostCat

+0

Hi @GhostCat, próbowałem już zbadać mój podstawowy problem za pomocą linii komentarza 'Chcę poprawić tę linię w taki sposób, żebym mógł wywołać poniższą metodę z rzeczywistą klasą {T rozszerza AbstractEntity }' Możesz także zapoznaj się z rozwiązaniem wysłanym przeze mnie, aby uzyskać lepsze rozwiązanie, jeśli masz ochotę odpowiedzieć. –

+0

Czy 'AbstractTestLoader' będzie bezpośrednią superklasą? – glee8e

Odpowiedz

4

W końcu, po kilku próbach ja po prostu prosty i pracy rozwiązanie, ale nadal jestem otwarty na inne usłyszeć najlepsze odpowiedzi, jeśli to możliwe. Dzięki

public abstract class AbstractTestLoader<T extends AbstractEntity<T>> { 

    abstract Class<T> getEntityType(); 

    public void onMessage(TextMessage message) throws Exception { 
     onMessage(message, getEntityType()); 
    } 

    public void onMessage(TextMessage message, Class<T> clazz) throws Exception { 
     //here my original logic will go 
    } 
} 
2

Java implements generics via type erasure, co oznacza, że ​​jest to tylko koncepcja kompilacji. Gdy program jest uruchomiony, nie ma różnicy między AbstractTestLoader<Foo> i AbstractTestLoader<Bar>.

Istnieje kilka obejść, takich jak ten, który został wykryty, gdzie klasa, która jest świadoma typu T w czasie kompilacji, może utworzyć rzeczywisty typ klasy. Ale nie ma sposobu, aby używając czystych generycznych odkryć, co typowy jest w czasie wykonywania.

+0

Właściwie jest sposób.Zobacz odpowiedź @ glee8e: –

+0

@FedericoPeraltaSchaffner, dziękuję za wzmiankę o mnie, ale ten znak @ nie uruchamia powiadomienia systemowego, ponieważ to ostatnie powoduje, że system myśli, że nazywasz kogoś nazwiskiem 'glee8e's'. przypomnienie rodzaju :) – glee8e

+0

@FedericoPeraltaSchaffner: odpowiedź Glee8e jest dobra i prawdopodobnie sprawdzi się dobrze w wielu przypadkach. Nie wydaje mi się, żeby to unieważniało moją odpowiedź: w moim umyśle kwalifikuje się to jako jedno z obejść, o których wspomniałem, "gdzie można stworzyć klasę, która jest świadoma typu" T "w czasie kompilacji, aby zapewnić rzeczywisty typ klasy . " W odpowiedzi Vishala robi to za pomocą abstrakcyjnej metody; w przypadku joye8e robi to wymagając, aby klasy konsumujące były deklarowane w określony sposób. – StriplingWarrior

5

Należy zauważyć, że podczas gdy ogólny język Java został wymazany w środowisku wykonawczym, istnieją ograniczone odwołania do api, aby je pobrać, jeśli są obecne w pliku klasy.

Oto szybkie rozwiązanie z tymi założeniu:

  1. AbstractTestLoader jest bezpośrednim klasy super.
  2. Podklasy nie używają symbolu wieloznacznego podczas deklarowania superklasy, np. podklasy takie jak ta class GenericLoader<T extends SubclassAbstractEntity<T>> extends AbstractTestLoader<T> nie istnieje.

Oto kod:

public class AbstractTestLoader<T extends AbstractEntity<T>> { 

    private static final ClassValue<Class<?>> TYPE_PARAMETER_CACHE = new ClassValue<Class<?>>() { 
     @Override 
     protected Class<?> computeValue(Class<?> type) { 
      assert AbstractTestLoader.class.isAssignableFrom(type); 
      assert type.getSuperclass() == AbstractTestLoader.class; 
      Type genericSuperclass = type.getGenericSuperclass(); 
      assert genericSuperclass instanceof ParameterizedType; 
      Type entityType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0]; 
      assert entityType instanceof Class<?>; 
      return (Class<?>) entityType; 
     } 
    }; 

    @SuppressWarnings("unchecked") 
    protected Class<T> getEntityType() { 
     return (Class<T>) TYPE_PARAMETER_CACHE.get(this.getClass()); 
    } 

    public void onMessage(Object message) throws Exception { 
     onMessage(message, getEntityType()); // getting compile time error here 
    } 

    public void onMessage(Object message, Class<T> clazz) throws Exception { 
     //here my original logic will go 
    } 
} 

getEntityType mogą być nadpisane w podklasach, gdzie te dwa założenia zawodzą.

Powiązane problemy