2013-03-07 10 views
10

W prosty interfejs:getMethods() Zwraca nie zdefiniowano przy wykonywaniu ogólnego interfejsu

interface Foo { 
    void myMethod(String arg); 
} 

class FooImpl implements Foo { 
    void myMethod(String arg){} 

    public static void main(String[] args) { 
    Class cls = FooImpl.class; 
    try { 
     for (Method method : cls.getMethods()) { 
     System.out.print(method.getName() + "\t"); 
     for(Class paramCls : method.getParameterTypes()){ 
      System.out.print(paramCls.getName() + ","); 
     } 
     System.out.println(); 
     } 
    } catch (SecurityException e) { 
     // TODO Auto-generated catch block 
    } 
    } 
} 

Wyjście to:

myMethod java.lang.String, 
...//Other Method 

tylko jeden myMethod wydrukowaniu.

Ale jeśli zmienić interfejs rodzajowe One:

interface Foo<T> { 
    void myMethod(T arg);  
} 

class FooImpl implements Foo<String> { 
    void myMethod(String arg){} 
} 

Potem dziwnie wyjście będzie:

myMethod java.lang.Object, 
myMethod java.lang.String, 
...//Other Method 

Dlaczego po zmianie interfejsu rodzajowego jednego doprowadzi do jeszcze jednej Metoda z typem parametru Obiekt?

Odpowiedz

6

Pierwsza metoda to bridge method, utworzona przez kompilator. Jeśli przetestujesz metody "isBridge()", możesz odfiltrować "niewłaściwe" metody (także odfiltrowuje dziwne wyniki, które możesz uzyskać, zwracając kowariancję).

następujący kod nie będzie drukować myMethod java.lang.Object:

import java.lang.reflect.Method; 


public class FooImpl implements Foo<String> { 
    public void myMethod(String arg) { 
    } 

    public static void main(String[] args) throws Exception { 
     Class cls = FooImpl.class; 
     for (Method method : cls.getMethods()) { 
      if (!method.isBridge()) { 
       System.out.print(method.getName() + "\t"); 

       for (Class paramCls : method.getParameterTypes()) { 

        System.out.print(paramCls.getName() + ","); 

       } 
      } 
      System.out.println(); 
     } 
    } 
} 

interface Foo<T> { 
    public void myMethod(T arg); 
} 
+0

Patrząc na „co jest metodą most” teraz. – StarPinkER

+1

Przeczytaj ten samouczek, który wyjaśnia wszystko: http://docs.oracle.com/javase/tutorial/java/generics/erasure.html – Muel

2
try { 
     for (Method method : cls.getMethods()) { 
    //these methods are called bridge methods  
      if(!method.isBridge()){ 
       System.out.print(method.getName() + "\t"); 
       for(Class paramCls : method.getParameterTypes()){ 
        System.out.print(paramCls.getName() + ","); 
       } 
       System.out.println(); 
      } 
     } 
    } catch (SecurityException e) { 
     // TODO Auto-generated catch block 
    } 

UPDATE:Bridge methods

Cytując blog:

metody most w Java są metody syntetyczne, które są konieczne do implementują niektóre funkcje języka Java. Najbardziej znane próbki to: kowariantny typ zwracany i przypadek w generycznych przypadkach, gdy usunięcie argumentów metody bazowej różni się od faktycznej wołanej metody.

UPDATE CODE z góry blogu:

public class SampleTwo { 
    public static class A<T> { 
     public T getT(T args) { 
      return args; 
     } 
    } 

    public static class B extends A<String> { 
     public String getT(String args) { 
      return args; 
     } 
    } 
} 

Na kompilacji kodu będzie wyglądać następująco:

public SampleThree$B(); 
... 
public java.lang.String getT(java.lang.String); 
Code: 
0: aload_1 
1: areturn 

public java.lang.Object getT(java.lang.Object); 
Code: 
0: aload_0 
1: aload_1 
2: checkcast  #2; //class java/lang/String 
5: invokevirtual #3; //Method getT:(Ljava/lang/String;)Ljava/lang/String; 
8: areturn 
} 

metoda most, który nadpisuje metodę z klasy bazowej „A ", a nie po prostu wywołując jeden z argumentem ciągu (# 3), ale także wykonuje typ odlewania do "java.lang.String" (nr 2). Oznacza to, że jeśli będzie wykonać poniższy kod, ignorując „niesprawdzony” ostrzeżenie kompilator, wynik będą ClassCastException wyrzucony z metodą mostka:

A a = new B(); 
a.getT(new Object())); 
+0

+1 dla linku do bloga. – StarPinkER

Powiązane problemy