2016-07-29 11 views
10

chciałbym napisać trochę kodu:Która metoda java.lang.Class generuje właściwe wejście dla Class.forName()?

Object o = ...; 
String oTypeName = o.getClass().getName(); 

//on the other side of the wire: 

Class<?> oClass = Class.forName(oTypeName); 
Object oAgain = oClass.newInstance(); 

Jednak nie jest jasne, z javadoc której metody należy użyć do zainicjowania oTypeName, tj która metoda przyniesie oczekiwane wejście do Class.forName():

  • getCanonicalName(): „Zwraca kanoniczną nazwę podstawowej klasy zgodnie z definicją języka Java Specification Zwraca null, jeśli podstawowa klasa nie posiada kanoniczną nazwę (czyli czy jest to lokalna lub anonimowa klasa lub tablica, której składnikiem. type nie ma nazwy kanonicznej). "
  • getName(): "Zwraca nazwę obiektu (klasa, interfejs, klasa tablicy, typ pierwotny lub puste pole) reprezentowaną przez ten obiekt klasy jako łańcuch .Jeśli ten obiekt klasy reprezentuje typ odniesienia, który nie jest typem tablicy następnie zwracana jest nazwa binarna klasy, zgodnie ze specyfikacją języka Java ™. "
  • getTypeName(): "Zwróć napis informacyjny dla nazwy tego typu."

Jest dość oczywiste, że nie chcę żadnego z nich:

  • getSimpleName() „Zwraca prostą nazwę klasy bazowej, jak podano w kodzie źródłowym”
  • toString(): „W reprezentacji ciąg jest ciąg«klasa»lub«interfejs», a następnie po spacji, a następnie w pełni kwalifikowaną nazwę klasy w formacie zwróconym przez getName”

I don Spodziewaj się, że to działa dla typów pierwotnych. Jest w porządku, jeśli nie zadziała dla tablic. Najważniejszą rzeczą, o którą się martwię, są zagnieżdżone klasy i Foo.Bar vs. Foo$Bar.

+1

Mam na myśli zagnieżdżone; pytanie zaktualizowane –

Odpowiedz

8

Ostateczna odpowiedź to getName(). Choć nieco ukryte, to jest określone w Javadoc z przeciążenia forName(className, initialize, loader):

Given w pełni kwalifikowana nazwa klasy lub interfejsu (w tym samym formacie zwróconym przez getName) metoda ta próbuje zlokalizować, załaduj i połącz klasę lub interfejs.

i to jest również określone, że nazywając forName(className) jest równoznaczne z wywołaniem tej przeciążenie, z wartościami domyślnymi:

Wywoływanie tej metody jest równoznaczne z:

Class.forName(className, true, currentLoader) 

gdzie currentLoader oznacza zdefiniowanie program ładujący klasy obecnej klasy.


Oto przykładowy kod pokazujący, że działa dla zagnieżdżonych klas, lokalnych klas, anonimowe klasy, prymitywnych lub obiektów tablic. Nie będzie działać dla prymitywów, ponieważ Class.forName nie obsługuje klas pierwotnych.

public class Main { 
    public static void main(String... args) throws ClassNotFoundException { 
     class LocalClass {} 

     System.out.println(Class.forName(name(StaticNestedClass.class))); //static nested class 
     System.out.println(Class.forName(name(InnerClass.class))); // inner class 
     System.out.println(Class.forName(name(Integer[].class))); // object array 
     System.out.println(Class.forName(name(int[].class))); // primitive array 
     System.out.println(Class.forName(name(List.class))); // interface 
     System.out.println(Class.forName(name(LocalClass.class))); // local class 
     System.out.println(Class.forName(name(new Object(){}.getClass()))); // anonymous class 
    } 

    private static String name(Class<?> clazz) { 
     return clazz.getName(); 
    } 

    public static class StaticNestedClass {} 
    public class InnerClass {} 
} 
+0

I odwrotnie, ani' getTypeName' ani 'getCanonicalName' działa na przykład na prymitywne tablice. Zarówno 'Class.forName (int []. Class.getTypeName())' jak i 'Class.forName (int []. Class.getCanonicalName())' rzuci 'ClassNotFoundException', ale' Class.forName (int []. class.getName()) 'nie będzie. – palimpsestor

+0

Dzięki za znalezienie, że pochowany odniesienia javadoc - to oficjalne słowo, którego szukałem i nie mogłem znaleźć! –

2

Wygląda albo getName() lub getTypeName() prac, przynajmniej w prostym przypadku:

public final class ForNameTest{ 

    public static void main(String[] args) throws Exception{ 
     Object o = new Foo(); 
     System.out.println("class is: " + o.getClass()); 

     for(String getterMethodName : Arrays.asList("getName", "getTypeName", "getCanonicalName")){ 
      Method m = Class.class.getMethod(getterMethodName); 
      String oTypeName = m.invoke(o.getClass()).toString(); 
      System.out.println(getterMethodName + " yields " + oTypeName); 
      try{ 
       Class<?> oType = Class.forName(oTypeName); 
       Object oAgain = oType.newInstance(); 
       System.out.println(" ... and it works: " + oAgain); 
      } catch (Exception e){ 
       System.err.println(" ... and it fails: " + e); 
      } 
     } 
    } 

    public static class Foo{} 
} 

Wyjście jest produkowany:

class is: class ForNameTest$Foo 
getName yields ForNameTest$Foo 
... and it works: [email protected] 
getTypeName yields ForNameTest$Foo 
... and it works: [email protected] 
getCanonicalName yields ForNameTest.Foo 
... and it fails: java.lang.ClassNotFoundException: ForNameTest.Foo 
+1

Dokładnie tam też dotarłem. Wow, myślisz, że wiesz coś (byłam ** pewna ** to była 'Foo.Bar'), a następnie testujesz to i, no cóż ... –

1

Zawsze używam getCanonicalName() obiekty wewnętrzne (jak twój Foo $ Bar Jeśli implementacja static public vs inline) również będzie możliwa.

Możesz także sprawić, żeby działało w/prymitywach. Na przykład istnieje "int.class". Jednak prawdopodobnie będziesz musiał sprawdzić klasy prymitywów i stworzyć instancję Object (Integer vs int), a następnie wywołać accessor jak intValue(). Z tego powodu używam wielu instancji Object i prymitywnych, ale to tylko moje preferencje.

-1
Object oAgain = oClass.newInstance(); 

[Edycja] bez względu na sposób (getName() getCanonicalName(), itp ..), które nie może zastosowanie newInstance() w celu utworzenia obiektu dla nie statycznego klasy wewnętrzną

Jeśli tworzysz obiekt za pomocą metody newInstance(), to jest obowiązkowe, aby klasa podstawowa zawierała konstruktor bez arg. Nawet jeśli wyraźnie wstawić jeden żadnego konstruktora argumentu, kompilator będzie przekonwertować go do konstruktora z niektórymi argumentami (Tylko w przypadku braku statycznej klasie wewnętrznej)

[END EDIT]

Poniżej znajduje się link do krótkiego kod, który znalazłem. Pokazuje to powyższe wyjaśnienie.

http://thecodersbreakfast.net/index.php?post/2011/09/26/Inner-classes-and-the-myth-of-the-default-constructor

+0

Nie jest konieczne, aby konstruktor no-arg był domyślnym konstruktorem. –

+1

Edytowałem odpowiedź, co mam na myśli, to znaczy, że metoda newInstance() oczekuje, że żaden konstruktor argumentów nie będzie obecny w twojej klasie. –

Powiązane problemy