2014-11-19 5 views
1

Я на 99% уверен, что это невозможно, но я подумал, что стоит попробовать: возможно ли создать класс, который реализует шаблон?Meta Generic в Java

Мотивация: Я хочу создать общий составной класс.

Пример:

public class GenericComposite<T> implements T { 
    List<T> m_items; 

    void add(T item) { 
     m_items.add(item); 
    } 

    @Override 
    void f() { 
     for (T item : m_items) { 
      item.f(); 
     } 
    } 
} 

где F является метод, определенный в интерфейсе T. Конечно, мы не знаем, что это собирается быть f(), когда мы называем его, ни где мы реализуем его. Есть ли какая-то мета-мета-магия, о которой я не знаю, что делает это возможным? Если нет, есть ли какой-нибудь другой язык, который поддерживает это?

+0

в Java, вы не знаете, что 'T' является' interface' против в 'class'. Не знаю, есть ли другие варианты языка. –

+1

Вы можете сделать это на C++, потому что 'GenericComposite ' - это шаблон - совсем другая вещь из Java generic. – dasblinkenlight

ответ

3

Вы правильны, это не представляется возможным в Java: компилятор не может даже быть уверенным, что T является interface (в отличие от class), поэтому он не может позволить вам реализовать T.

C++ предлагает эту возможность с помощью шаблонов, но даже это не поможет вам реализовать «общий составной». Проблема заключается в методе f() (или методах в фактическом классе, для которого он стоит). То, что вы хотели написать это:

// This is not going to work! The syntax is not real. 
public class GenericComposite<T> implements T { 
    List<T> m_items; 

    void add(T item) { 
     m_items.add(item); 
    } 

    @Override 
    <all-methods-of-T>(); 
} 

Для того, чтобы написать @Override void f() вы должны знать, что T имеет void f(). Вы должны знать об этом каждый метод T, который, конечно же, побеждает цель скрыть личность T: ваш GenericComposite<T> знает все, что нужно знать об интерфейсе, т. Е. Все его методы.

Когда вы создаете композит, наличие списка подэлементов является гораздо меньшей проблемой, чем реализация методов, позволяющих вызывающим абонентам обрабатывать композицию так же, как они обрабатывают листовые объекты. Все эти «совокупные» методы всегда являются обычными, что делает общие структурные реализации композита почти бесполезными.

0

Да, вы можете это сделать.

public interface Thing { 
    void something(); 
} 

public Composite<T implements Thing> implements Thing { 
    private List<T> things = new ArrayList<>(); 

    @Override 
    public void something() { 
     for(Thing aThing:things) { 
      aThing.something(); 
     } 
    } 
} 

Однако, учитывая, что все имеет общий интерфейс, вам действительно нужны общие для решения проблемы?

+3

В отличие от вопроса OP, где 'T' может быть чем угодно, а' f() 'является методом' T', вы фиксируете 'f()', чтобы находиться в 'Thing'.По сути, ваш 'Composite' реализует' Thing', а не 'T'. – dasblinkenlight

+0

Я начал писать, почему это не то, что я искал, но @dasblinkenlight выразился яснее, чем то, как я бы – wolfovercats

+0

Он должен сказать: '' – Sebastian

1

Да, вы можете сделать это на Java с помощью Proxys и отражения. Я делал это раньше. Дополнительную проблему, о которой вы должны беспокоиться, - это методы, возвращающие значения. Если вы завершаете несколько реализаций, возвращающих значение, вы действительно возвращаетесь?

В моем решении, я использую перечисление сказать класс либо возвращать первый результат из списка или последнего результата (в любом случае все делегаты называются)

Вот мой код

/** 
* {@code MultipleWrapper} uses dymanic proxies to wrap 
    * several instances of an interface. This allows 
* all wrapped instances to be called by only a single 
* call to the wrapper. 
* @author dkatzel 
*/ 
public final class MultipleWrapper<T> implements InvocationHandler{ 

private final ReturnPolicy policy; 
private final List<T> delegates = new ArrayList<T>(); 
/** 
* Since methods can only return a single 
* return value, only one of the wrapped 
* methods can be returned to the caller (even though 
* they will all be called). 
* @author dkatzel 
*/ 
public static enum ReturnPolicy{ 
    /** 
    * Return the first wrapped instance. 
    */ 
    RETURN_FIRST, 
    /** 
    * Return the last wrapped instance. 
    */ 
    RETURN_LAST 
} 
/** 
* Create a dynamic proxy to wrap the given delegate instances. 
* @param <T> the interface to proxy. 
* @param <I> the instances of T. 
* @param classType the class object of T. 
* @param policy the return policy to use on methods that return something. 
* @param delegates the list of delegates to wrap in the order in which 
* they will be called. 
* @return a new instance of T that wraps the delegates. 
* @throws IllegalArgumentException if no delegates are given 
* @throws NullPointerException if classType ==null or policy ==null or any delegate ==null. 
*/ 
@SuppressWarnings("unchecked") 
public static <T, I extends T> T createMultipleWrapper(Class<T> classType,ReturnPolicy policy, Iterable<I> delegates){ 

    return (T) Proxy.newProxyInstance(classType.getClassLoader(), new Class<?>[]{classType}, 
      new MultipleWrapper<T>(policy,delegates)); 
} 
/** 
* Convenience constructor which is the same as calling 
* {@link #createMultipleWrapper(Class, ReturnPolicy, Object...) 
* createMultipleWrapper(classType,ReturnPolicy.RETURN_FIRST,delegates)} 
* @see #createMultipleWrapper(Class, ReturnPolicy, Object...) 
*/ 
public static <T,I extends T> T createMultipleWrapper(Class<T> classType,Iterable<I> delegates){ 
    return createMultipleWrapper(classType,ReturnPolicy.RETURN_FIRST,delegates); 
} 
/** 
* Convenience constructor which is the same as calling 
* {@link #createMultipleWrapper(Class, ReturnPolicy, Object...) 
* createMultipleWrapper(classType,ReturnPolicy.RETURN_FIRST,delegates)} 
* @see #createMultipleWrapper(Class, ReturnPolicy, Object...) 
*/ 
@SafeVarargs 
public static <T,I extends T> T createMultipleWrapper(Class<T> classType,I... delegates){ 
    return createMultipleWrapper(classType,ReturnPolicy.RETURN_FIRST,Arrays.asList(delegates)); 
} 
/** 
* Convenience constructor which is the same as calling 
* {@link #createMultipleWrapper(Class, ReturnPolicy, Object...) 
* createMultipleWrapper(classType,ReturnPolicy.RETURN_FIRST,delegates)} 
* @see #createMultipleWrapper(Class, ReturnPolicy, Object...) 
*/ 
@SafeVarargs 
public static <T,I extends T> T createMultipleWrapper(Class<T> classType,ReturnPolicy policy,I... delegates){ 
    return createMultipleWrapper(classType,policy,Arrays.asList(delegates)); 
} 


private MultipleWrapper(ReturnPolicy policy,Iterable<? extends T> delegates){ 
    if(policy==null){ 
     throw new NullPointerException("policy can not be null"); 
    } 

    this.policy = policy; 
    for(T delegate : delegates){ 
     if(delegate ==null){ 
      throw new NullPointerException("delegate can not be null"); 
     } 
     this.delegates.add(delegate); 
    }  
    if(this.delegates.size()==0){ 
     throw new IllegalArgumentException("must wrap at least one delegate"); 
    } 
} 
@Override 
public Object invoke(Object proxy, Method method, Object[] args) 
     throws Throwable { 
    List<Object> returns = new ArrayList<Object>(delegates.size()); 
    try{ 
     for(T delegate :delegates){ 
      returns.add(method.invoke(delegate, args)); 
     } 
     if(policy == ReturnPolicy.RETURN_LAST){ 
      return returns.get(returns.size()-1); 
     } 
     return returns.get(0); 
    }catch(InvocationTargetException e){ 
     throw e.getCause(); 
    } 

} 

} 

Затем, чтобы использовать его вы можете сделать что-то вроде этого:

List<Type> listeners = ... 

Type wrappedListener = MultipleWrapper.createMultipleWrapper(Type.class, listeners);