2008-12-10 25 views
13

Có thể phản ánh nhanh một loại chung trong Java không? Sử dụng kỹ thuật được mô tả here Tôi gặp lỗi vì mã thông báo lớp học không thể là chung chung. Lấy ví dụ dưới đây. Tôi muốn khởi tạo một số lớp con của Creator để triển khai Creator. Tên lớp thực tế được truyền vào như một đối số dòng lệnh. Ý tưởng là để có thể chỉ định việc triển khai Trình tạo khi chạy. Có cách nào khác để hoàn thành những gì tôi đang cố gắng làm ở đây không?Tôi có thể phản ánh nhanh kiểu generic trong java không?

public interface Creator<T> { 
    T create(); 
} 
public class StringCreator implements Creator<String> { 
    public String create() { return new String(); } 
} 
public class FancyStringCreator implements Creator<String> { 
    public String create() { return new StringBuffer().toString(); } 
} 
public static void main(String[] args) throws Exception { 
    Class<?> someClass = Class.forName(args[0]); 
    /*ERROR*/Class<? extends Creator<String>> creatorClass = someClass.asSubclass(Creator.class); 
    Constructor<? extends Creator<String>> creatorCtor = creatorClass.getConstructor((Class<?>[]) null); 
    Creator<String> creator = creatorCtor.newInstance((Object[]) null); 
} 

Chỉnh sửa: Tôi thích cách tiếp cận của Marcus là đơn giản nhất và thực dụng mà không phá vỡ toàn bộ điều. Tôi có thể sử dụng nó trong tình huống của mình vì tôi có thể chỉ định rằng lớp được truyền phải là một lớp con của StringCreator. Nhưng như Ericson đã chỉ ra các thông tin chung vẫn còn ở cấp độ loại, không phải ở cấp độ thời gian chạy nên vẫn có thể kiểm tra một cách phản xạ xem một lớp cụ thể có triển khai đúng kiểu generic hay không.

Trả lời

7

Thông tin chung sẽ bị mất trong thời gian chạy. Không có thời gian chạy tương đương với một chuỗi < Creator> .class. Bạn có thể tạo ra một loại giữa Đấng Tạo Hóa và StringCreator mà sửa chữa các loại generic:

public interface Creator<T> { 
     T create(); 
} 
public interface StringCreator extends Creator<String> { } 
public class StringCreatorImpl implements StringCreator { 
     public String create() { return new String(); } 
} 
public class FancyStringCreator implements StringCreator { 
     public String create() { return new StringBuffer().toString(); } 
} 
public static void main(String[] args) throws Exception { 
     Class<?> someClass = Class.forName(args[0]); 
     Class<? extends StringCreator> creatorClass = someClass.asSubclass(StringCreator.class); 
     Constructor<? extends StringCreator> creatorCtor = creatorClass.getConstructor((Class<?>[]) null); 
     Creator<String> creator = creatorCtor.newInstance((Object[]) null); 
} 

Nhưng tất nhiên bạn sẽ mất một chút linh hoạt, bởi vì bạn không thể sử dụng lớp tác giả sau đây:

public class AnotherCreator implements Creator<String> { 
    public String create() { return ""; } 
} 
+0

Không phải tất cả thông tin chung đều bị mất khi chạy; còn đủ để kiểm tra thông tin loại cần thiết ngay cả khi không tạo giao diện khác. Nó chỉ là không có đủ thông tin còn lại cho trình biên dịch để suy ra tính chính xác. – erickson

+0

Bạn đang ở đó. Những gì tôi đã nói (một cách phức tạp) là không có đối tượng lớp nào đại diện cho Người sáng tạo khác biệt với Người sáng tạo . Nhưng mã của bạn được cắt dưới đây có vẻ như là một cách hợp lý để giải quyết vấn đề. – Markus

3

Bạn không cần dòng đó. Bạn cũng không cần hàm tạo vì bạn chỉ sử dụng hàm mặc định. Chỉ cần nhanh chóng lớp trực tiếp:

public static void main(String[] args) throws Exception { 
     Class<?> someClass = Class.forName(args[0]); 
     Creator<String> creator = (Creator<String>) someClass.newInstance(); 
} 

Nếu bạn nhấn mạnh, bạn sẽ chỉ có thể để có được nửa đường:

public static void main(String[] args) throws Exception { 
    Class<?> someClass = Class.forName(args[0]); 
    Class<? extends Creator> creatorClass = someClass.asSubclass(Creator.class); 
    Constructor<? extends Creator> creatorCtor = creatorClass.getConstructor((Class<?>[]) null); 
    Creator<String> creator = (Creator<String>) creatorCtor.newInstance((Object[]) null); 
} 
+0

claz.newInstance() và ctor.newInstance() không hoạt động chính xác như cũ khi xảy ra lỗi.Phương thức newInstance của lớp cũ hơn và không phù hợp với các lời gọi "phương thức" phản chiếu khác. – erickson

-1

Không khá chắc chắn lý do tại sao bạn đang sử dụng Generics đây.

Việc khởi tạo đối tượng sử dụng phản chiếu sẽ gợi ý sử dụng chung nhưng có lẽ bạn sẽ gọi create tại một thời điểm nào đó và gán kết quả cho String, nếu không thì hãy sử dụng Generics để kiểm soát kiểu trả về.

Nhưng nếu bạn viết thực hiện sau đây của Đấng Tạo Hóa:

public class IntegerCreator implements Creator<Integer> 
{ 
    public Integer create() 
    { 
    ... 
    } 
} 

Và thông qua nó như là một đối số bạn muốn có được một ClassCastException khi gọi create và gán kết quả.

4

Điều này sẽ làm những gì bạn đang cố gắng làm trong khi cung cấp sự an toàn về loại. Không có cách nào để tránh một cảnh báo không được kiểm soát, nhưng loại kiểm tra được thực hiện ở đây biện minh cho sự đàn áp của nó.

public static void main(String[] args) 
    throws Exception 
    { 
    Class<? extends Creator<String>> clz = load(argv[0], String.class); 
    Constructor<? extends Creator<String>> ctor = clz.getConstructor(); 
    Creator<String> creator = ctor.newInstance(); 
    System.out.println(creator.create()); 
    } 

    public static <T> Class<? extends Creator<T>> load(String fqcn, Class<T> type) 
    throws ClassNotFoundException 
    { 
    Class<?> any = Class.forName(fqcn); 
    for (Class<?> clz = any; clz != null; clz = clz.getSuperclass()) { 
     for (Object ifc : clz.getGenericInterfaces()) { 
     if (ifc instanceof ParameterizedType) { 
      ParameterizedType pType = (ParameterizedType) ifc; 
      if (Creator.class.equals(pType.getRawType())) { 
      if (!pType.getActualTypeArguments()[0].equals(type)) 
       throw new ClassCastException("Class implements " + pType); 
      /* We've done the necessary checks to show that this is safe. */ 
      @SuppressWarnings("unchecked") 
      Class<? extends Creator<T>> creator = (Class<? extends Creator<T>>) any; 
      return creator; 
      } 
     } 
     } 
    } 
    throw new ClassCastException(fqcn + " does not implement Creator<String>"); 
    } 

Hạn chế chính bạn phải tuân thủ là một lớp trong cấu trúc phân cấp phải chỉ định tham số loại. Ví dụ: class MyCreator implements Creator<String>. Bạn không thể sử dụng nó với class GenericCreator<T> implements Creator<T>.

Hiện tại, nó không xử lý trường hợp hợp lệ khi bạn tạo giao diện mới interface StringCreatorIfc extends Creator<String> và yêu cầu một lớp thực hiện điều đó. Nó có thể được tăng cường để làm điều đó, nhưng tôi sẽ để lại đó như là một bài tập cho những người nghiêng.

Các vấn đề liên quan