2012-03-22 50 views
6

Tôi có một Enum gọi Plugins:Khởi tạo một đối tượng mới từ lớp trong Enum

public enum Plugins { 

    ROTATING_LINE (plugin.rotatingline.RotatingLine.class), 
    SNOW_SYSTEM (plugin.snow.SnowSystem.class); 

    private Class<?> c; 

    private Plugins (Class<?> c) { 
     this.c = c; 
    } 

    public Class<?> getClassObject() { 
     return c; 
    } 

} 

Những gì tôi muốn làm là để lặp qua tất cả các enums trong Plugins và tạo các đối tượng mới từ những người sử dụng biến c như thế này:

for (Plugins plugins : Plugins.values()) { 
    Class<?> c = plugins.getClassObject(); 
    pluginList.add(new c(400, 400)); 
} 

Có cách nào để hoàn thành điều này bằng phương pháp tương tự không? Lý do tại sao tôi muốn làm điều này là tạo một danh sách các lớp cần được thêm vào các Plugin danh sách khi tôi khởi động ứng dụng của mình.

+1

Danh sách này có ở đâu? Bạn không thể tự động thêm giá trị vào một enum. –

+0

Tên được đặt tên giống nhau do nhầm lẫn. plugins.add đề cập đến một danh sách được gọi là plugins và Plugins là enum. –

Trả lời

11

Vì bạn đang lặp qua các plugin, tôi đoán tất cả các nhà thầu của plugin có cùng số & loại tham số. (Tôi không hiểu plugins.add(...) của bạn mặc dù.)

Anyways, nếu bạn muốn sử dụng Enums, tôi sẽ làm những điều sau đây, sử dụng triển khai phương pháp liên tục cụ thể thay vì loại thẻ/phản ánh:

public enum PluginTypes { 
    ROTATING_LINE { Plugin create(int x, int y){ 
     return new plugin.rotatingline.RotatingLine(x,y);} }, 
    SNOW_SYSTEM { Plugin create(int x, int y){ 
     return new plugin.snow.SnowSystem(x,y);} }; 

    abstract Plugin create(int x, int y); 
} 

public interface Plugin { 
    //... 
} 

Sau đó, vòng lặp của bạn sẽ giống như thế này:

List<Plugin> plugins = new ArrayList<Plugin>(); 

for (PluginTypes pt : PluginTypes.values()) { 
    plugins.add(pt.create(400, 400)); 
} 

Mặt khác, nếu bạn biết các lớp học thực hiện của bạn của Plugin anyways, tại sao không sử dụng chúng trực tiếp thay vì thông qua PluginType s Enum?

+0

Đây là câu trả lời tôi đã đi, dường như tốt nhất cho mục đích của tôi. Cảm ơn mặc dù cho tất cả các câu trả lời khác, cả hai đã học được rất nhiều và bạn đã cho tôi rất nhiều cảm hứng! –

+0

Vui vì tôi có thể giúp đỡ.Bạn có thể cho biết điều này bằng cách chấp nhận câu trả lời;) Và nếu bạn thay đổi ý định sau này, bạn có thể hoàn tác/chuyển đổi câu trả lời bạn thấy tốt nhất. – DaveFar

+0

Tôi mới ở đây, không thấy tùy chọn đó trước đây. Xong rồi, tôi hy vọng (?)! Cám ơn bạn một lần nữa. –

2

Trước tiên, bạn cần tất cả các lớp học để thực hiện một số loại giao diện:

public interface Plugin { 
    public doSomething(); 
} 

Sau đó, bạn có thể làm

Class clazz = getClassObject(); 
Plugin plugin = clazz.newInstance(); 
... add to a list where you can use later ... 

Về tải plugin của bạn, bạn có thể khai báo tất cả trong số họ trong một enum ... hoặc bạn có thể khai báo chúng trong cấu hình .xml hoặc .properties (ví dụ) sẽ cung cấp cho bạn sự linh hoạt hơn:

public void loadPlugins(){ 
    List<Plugin> plugins = new ArrayList<Plugin>();  
    ... load plugin names from config file ... 
    for(String pluginClass : pluginsConfigList) 
     plugins.add(Class.forName(pluginClass).newInstance()); 
} 

Tất nhiên, bạn sẽ cần một số ngoại lệ xử lý cơ bản, vv - mã này là được viết bằng một vội vàng từ những gì tôi nhớ làm (:

+1

+1 cho điều này, cách khác bạn có thể sử dụng [cơ chế ServiceLoader] (http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html) – Paaske

+3

Dường như OP muốn sử dụng ntorefault constructor, do đó, 'newInstance' sẽ không hoạt động trực tiếp. –

1

Vâng, bạn có thể làm điều đó bằng suy nghĩ. Trong thực tế, chúng tôi đang làm gần như chính xác như nhau trong một enum trong dự án của chúng tôi. Nó hoạt động độc đáo, mặc dù tôi không thoải mái 100% với tất cả các phụ thuộc trực tiếp mà giải pháp này tạo ra. Có thể tốt hơn bằng cách nào đó sử dụng DI qua ví dụ: Mùa xuân, tuy nhiên điều này đã không bugged tôi đủ để thực sự thử nghiệm với một giải pháp.

Nếu lớp có hàm tạo mặc định, chỉ cần gọi c.newInstance(). Nếu không, vấn đề phức tạp hơn một chút - here is another post giải quyết vấn đề này.

Tất nhiên, một khi bạn có đồ vật, bạn cần phải làm gì đó với họ. Cách tiêu chuẩn là có tất cả các lớp tham gia thực hiện cùng một giao diện, sau đó bạn có thể bỏ các đối tượng xuống giao diện đó và gọi các phương thức giao diện trên chúng. (Tất nhiên, trong mã sản xuất, bạn cần nắm bắt và xử lý tất cả các trường hợp ngoại lệ có thể xảy ra có thể xảy ra - chẳng hạn như InstantiationException, IllegalAccessExceptionClassCastException).

0

Có xung đột trong đoạn mã thứ hai gây nhầm lẫn cho tôi một chút ... biến plugins được sử dụng cho cả hai hằng số enum và dưới dạng danh sách xuất hiện. Vì vậy, tôi giả định điều này, nhưng bạn nên đăng đoạn mã thực sự hoạt động trong tương lai.

Vì vậy, vâng, có một cách để làm những gì bạn muốn:

for (Plugins plugins : Plugins.values()) { 
    Class<?> c = plugins.getClassObject(); 
    pluginsList.add(c.getConstructor(Integer.TYPE, Integer.TYPE).newInstance(400, 400)); 
} 

Ngoài ra tôi khuyên bạn nên nhìn vào Service Loader, mà là một công cụ thực sự mát mẻ để tự động tải các dịch vụ từ classpath.

+0

Có, trong sự vội vàng của mã dán tôi đổi tên nó để làm cho nó rõ ràng hơn, tôi vấp và đặt tên một số variabels thực sự xấu. Lấy làm tiếc. –

0

Để tạo thể hiện của lớp bạn có thể theo dõi câu trả lời từ @Peter và để giữ tham chiếu đến đối tượng đó, tôi đề xuất EnumMap.

EnumMap<Plugins, Object> map = new EnumMap<Plugins, Object>(Plugins.class); 
for (Plugins plugins : Plugins.values()) { 
    Class<?> c = plugins.getClassObject(); 
    map.put(plugins, c.newInstance()); 
} 
3

Tạo một giao diện cho các plugin

public interface Plugin{ 
    setShapeType(); 
    setX(); 
    setY(); 
    ... 
    } 

Tạo một phương thức trên enum mà tạo ra một thể hiện của Plugin.

public enum Plugins { 

     ROTATING_LINE (plugin.rotatingline.RotatingLine.class), 
     SNOW_SYSTEM (plugin.snow.SnowSystem.class); 

     private Class<? extends Plugin> c; 

     private Plugins (Class<? extends Plugin> c) { 
      this.c = c; 
     } 

     // This acts as a constructor that takes args, I am assuming the args you need 
     public Class<? extends Plugin> createInstance(String shapeType,int x, int y) { 
      Plugin plugin = c.newInstance(); 
      plugin.setShapeType(shapeType); 
      plugin.setX(x); 
      plugin.setY(y); 
      return plugin; 
     } 

    } 

nhanh chóng trong vòng lặp

List<Plugin> myPlugins = new ArrayList<Plugin>(); 

for (Plugins plugins : Plugins.values()) { 
    myPlugins.add(plugin.createInstance(Shaped.parent, 400, 400)); 
} 

Xin lưu ý đây là mã giả

2

Enums trong situtation này sẽ làm cho cuộc sống trở nên khó khăn hơn.

Trong quá khứ tôi đã giải quyết vấn đề này theo cách này:

class PluginFactory<T extends Plugin> { 

     private Class<T> clazz; 

     PluginFactory(Class<T> clazz) { 
      this.clazz = clazz; 
     } 

     T newInstance() { 
      return clazz.newInstance(); 
     } 

    final static PluginFactory<SnowSystem> SNOW_SYSTEM_FACTORY = 
      new PluginFactory(SnowSystem.class); 
    ... 

    // should really use a list and a wilcard generic bounded by Plugin, but it's 
    // too verbose for me for the purposes of this answer 
    final static PluginFactory[] FACTORIES = {SNOW_SYSTEM_FACTORY, ...}; 

    public static void main(String[] args) { 
     Plugin[] arr = new Plugin[FACTORIES.length]; 
     for (int i = 0; i < arr.length; i++) { 
      arr[i] = FACTORIES[i].newInstance(); 
     } 

     // more usefully though 
     SnowSystem ss = SNOW_SYSTEM_FACTORY.newInstance(); 
     ss.setValue(123); 
    } 

} 

Các tùy chọn khác là để cho newInstance var args đối tượng tham số. Sau đó sử dụng các phản xạ để tìm ra hàm tạo thích hợp có các kiểu này làm tham số. Điều này là vô cùng lộn xộn và hoàn toàn không an toàn nếu người dùng API cung cấp một tập hợp các đối số không hợp lệ (ngoại lệ được ném).

public T newInstance(Object... args) {  
    for (Constructor c : clazz.getConstructors()) { 
     if (isMatchingConstructor(c, args)) { 
      return clazz.cast(c.newInstance(args)); 
     } 
    } 
    throw new NoSuchMethodException("No matching constructor found for args: " 
      + Arrays.toString(args)); 
} 

private boolean isMatchingConstructor(Constructor c, Object... args) { 
    Class<?>[] parameters = c.getParameterTypes(); 

    if (parameters.length != args.length) { 
     return false; 
    } 

    for (int i = 0; i < args.length; i++) { 
     if (!parameters[i].isAssignableFrom(args[i].getClass())) { 
      return false; 
     } 
    } 
    return true; 
} 
+0

Sử dụng tốt các Nhà máy (+1). Bạn có biết rằng hình phạt hiệu suất hiện tại của tra cứu phương pháp phản xạ (với JITing và không có)? Tôi thường thấy các phép đo khá khác nhau, ví dụ: www.jguru.com/faq/view.jsp?EID=246569 – DaveFar

+0

Hình phạt đến từ việc phải thực hiện tìm kiếm ban đầu của phương thức/hàm tạo/trường, ví dụ: 'clazz.getConstructors()'. Overhead của thực sự gọi một phương thức/constructor thông qua phản xạ là tối thiểu. Vì vậy, nếu bạn có thể cache các Methods/Constructors/Fields được trả về thì hiệu suất hit là không đáng kể. – Dunes

+1

Bài đăng bạn liên kết là cổ đại (từ thời điểm java 1.3!). Hãy đọc http://stackoverflow.com/questions/414801/any-way-to-further-optimize-java-reflective-method-invocation – Dunes

0

Có 2 cách:

  1. sử dụng chức năng Enum.valueOf() tĩnh, sau đó quăng nó vào kiểu enum của bạn.

    Enum v = Enum.valueOf(TheEnumClass, valueInString); 
    
  2. Sử dụng chức năng class.getEnumConstants() để lấy danh sách các hằng số enum và lặp lại danh sách này và nhận.

    Plugins[] plugins = Plugins.class.getEnumConstants(); 
    for (Plugins plugin: plugins) { 
        // use plugin.name() to get name and compare 
    } 
    
Các vấn đề liên quan