2009-02-26 41 views
32

Trong C# tôi có thể làm thực sự này:phương pháp Generic trong Java mà không generic luận

//This is C# 
static T SomeMethod<T>() where T:new() 
{ 
    Console.WriteLine("Typeof T: "+typeof(T)); 
    return new T(); 
} 

//And call the method here 
SomeMethod<SomeClassName>(); 

Nhưng đối với một số lý do tôi không thể có được nó để làm việc trong Java.

Điều tôi muốn làm là tạo một phương thức tĩnh trên một lớp cha, vì vậy các lớp con có thể được chuyển đổi thành XML.

//This is Java, but doesn't work 
public static T fromXml<T>(String xml) { 
    try { 
    JAXBContext context = JAXBContext.newInstance(T.class); 
    Unmarshaller um = context.createUnmarshaller(); 
    return (T)um.unmarshal(new StringReader(xml)); 
    } catch (JAXBException je) { 
    throw new RuntimeException("Error interpreting XML response", je); 
    } 
} 

//Also the call doesn't work... 
fromXml<SomeSubObject>("<xml/>"); 

Trả lời

48
public static <T> T fromXml(Class<T> clazz, String xml) { 

gọi như:

Thing thing = fromXml(Thing.class, xml); 

hoặc rõ ràng hơn:

Thing thing = MyClass.<Thing>fromXml(Thing.class, xml); 

Để được thậm chí nhiều hơn khó hiểu bạn có thể có cấu trúc cho cả hai xây dựng một kiểu generic và có một generic thông số. Không thể nhớ cú pháp và chưa bao giờ nhìn thấy nó được sử dụng trong sự tức giận (bạn có lẽ tốt hơn hết với một phương pháp tạo tĩnh anyway).

Dàn diễn viên (T) không an toàn và bạn không thể viết T.class. Vì vậy, bao gồm các lớp như là một đối số (như JAXBContext.newInstance hiện) và ném một ngoại lệ có liên quan nếu loại là sai.

public static <T> T fromXml(Class<T> clazz, String xml) { 
    try { 
     JAXBContext context = JAXBContext.newInstance(clazz); 
     Unmarshaller um = context.createUnmarshaller(); 
     Object obj = um.unmarshal(new StringReader(xml)); 
     try { 
      return clazz.cast(obj); 
     } catch (ClassCastException exc) { 
      throw new RelevantException(
       "Expected class "+clazz+ 
        " but was "+obj.getClass() 
      ); 
     } 
    } catch (JAXBException exc) { 
     throw new RelevantException(
      "Error unmarshalling XML response", 
      exc 
     ); 
    } 
} 

Tôi tin rằng phiên bản tiếp theo của JAXB (trong 6u14?) Có một số phương pháp thuận tiện cho các loại điều này trong lớp JAXB.

+2

thậm chí sẽ không biên dịch ... Bạn không thể viết T.class – pgras

+2

Tôi không có nghĩa là viết T.class trong phương thức. Tôi đã nói như một kẻ thù. Và chức năng gọi có thể không (hoặc có thể nó đã được chuyển qua như một argumnet). –

+0

The newInstance (T.class) là một bản sao và dán mà tôi không thấy. Vẫn còn câu hỏi có vẻ là về cách viết một phương pháp chung chung. –

5

Trong Java, Generics là dữ liệu chỉ thời gian biên dịch, bị mất khi chạy. Vì vậy, nếu bạn gọi một phương pháp như vậy, JVM sẽ không có cách nào để biết những gì T.class được. Cách thông thường để vượt qua điều này là chuyển đối tượng thể hiện lớp như tham số cho phương thức, như sau:

public static <T> T fromXml(Class<T> clazz, String xml) { 
    try { 
    JAXBContext context = JAXBContext.newInstance(clazz); 
    Unmarshaller um = context.createUnmarshaller(); 
    return (T)um.unmarshal(new StringReader(xml)); 
    } catch (JAXBException je) { 
    throw new RuntimeException("Error interpreting XML response", je); 
    } 
} 

fromXml(SomeSubObject.class, "<xml/>"); 
+0

Vẫn không phải là cách để viết một phương pháp chung chung. –

+0

Hầu như. Các cần phải được ngay sau khi "tĩnh", không phải "fromXml" tôi nghĩ. – doekman

+0

Không lý tưởng, không. Nhưng nếu anh ta cần truy cập vào thông tin kiểu tại thời gian chạy (để gọi JAXBContext.newInstance()), anh ta sẽ cần một đối tượng của lớp đó hoặc đối tượng lớp. – Avi

1

Tôi sợ những gì bạn đang cố gắng làm sẽ không hoạt động trong Java. Việc không thể tạo các thể hiện kiểu generic mới là một trong những tính năng "phải có" mà .NET cung cấp trong khi Java chỉ đơn giản là thiếu. Điều này dẫn đến "cách giải quyết" như những đề xuất trước đó. Kiểm tra các ArrayList.toArray(T) như một tham chiếu như thế nào điều này có thể được thực hiện: về cơ bản bạn sẽ phải vượt qua một tham chiếu đến một đối tượng mà bạn đang cố gắng tạo ra để bạn biết những gì lớp để instantiate lúc runtime. Đây là mã từ ArrayList.toArray(T):

 

public <T> T[] toArray(T[] a) 
{ 
    if (a.length < size) 
    { 
     a = (T[]) java.lang.reflect.Array. 
     newInstance(a.getClass().getComponentType(), size); 
    } 
    System.arraycopy(elementData, 0, a, 0, size); 
    if (a.length > size) 
    { 
     a[size] = null; 
    } 
    return a; 
} 
 
2

Các phương pháp như Java Collections.emptySet() có một chữ ký như thế này:

public static final <T> Set<T> emptySet() 

Và được gọi là như thế này:

Set<Foo> foos = Collections.<Foo>emptySet(); 

phương pháp anyObject() Mockito là một ví dụ khác . Cá nhân tôi không tìm thấy một trong hai cú pháp rất tuyệt vời. Việc chuyển kiểu như là một đối số phương thức hoạt động nhưng luôn cảm thấy kludgy. Cung cấp tham số theo cách mà emptySet() có vẻ rõ ràng hơn nhưng không phải lúc nào cũng rõ ràng rằng phương thức cho phép loại được chỉ định.

1

Câu trả lời hiện tại an toàn hơn nhưng về mặt kỹ thuật đã lỗi thời vì trong java7 và hơn thế nữa, bạn không cần đối số "Class clazz". Loại được suy ra từ kiểu trả về dự kiến.Bạn chỉ nhận được cảnh báo về việc truyền không an toàn.

public class Main { 

    private static class Dog { 
     public String toString() { return "dog"; } 
    } 

    private static class Cat { 
     public String toString() { return "cat"; } 
    } 

    public static void main(String[] args) { 
     Cat cat = parse("cat"); 
     Dog dog = parse("dog"); 
     System.out.println("the cat object is a " + cat); 
     System.out.println("the dog object is a " + dog); 
    } 

    private static Object untypedParse(String stringToParse) { 
     if(stringToParse.equals("dog")) { 
      return new Dog(); 
     } else if(stringToParse.equals("cat")) { 
      return new Cat(); 
     } else { 
      throw new RuntimeException("not expected"); 
     } 
    } 

    public static <T> T parse(String stringToParse) { 
     return (T)untypedParse(stringToParse); 
    } 

} 


~/test/generics$ javac Main.java 
Note: Main.java uses unchecked or unsafe operations. 
Note: Recompile with -Xlint:unchecked for details. 
~/test/generics$ java Main 
the cat object is a cat 
the dog object is a dog 
Các vấn đề liên quan