2013-12-19 3 views
0

Я хочу сделать рефакторинг и хочу создать общий класс для избежания дублирования кода. У нас есть много XXXCriteriaValidator в нашем проекте, и мы хотим создать единственный уникальный класс, который заменит их все.Enum static Метод, вызываемый из класса Generic

Проблема заключается в одной строке, где этот класс требует статического метода из Enum. Здесь вы увидите. Это более или менее то, что я хочу достичь:

public class GenericCriteriaValidator<T extends ¿SomeKindOfEnumInterface?> 
               implements CriterionVisitor { 

    protected Errors errors; 

    public Errors getErrors() { 
     return this.errors; 
    } 

    /* 
    * Some code around here 
    */ 

    protected void doVisit(final PropertyCriterion criterion) { 
     if (criterion == null) { 
      this.errors.reject("error.criterion.null"); 
     } else { 
      if (criterion.getOperator() == null) { 
       this.errors.reject("error.operator.null"); 
      } 

      // Validates property (exception thrown if not exists) 
      T.fromString(criterion.getName()); // The problem is this call here!! 
               // Not saying this compiles, just looking 
               // how to do something equivalent 
     } 
    } 
} 

T всегда отличается от Enum. Типичное перечисление, как это:

public enum ContactCriteria implements CriteriaInterface<ContactCriteria> { 
             //^This interface is added by me 
             // for the enum being called in the previous       class 

    CONTACT_ID("this.id"), 
    CONTACT_COMPANY_ID("this.companyId"), 
    CONTACT_NAME("this.name"), 
    CONTACT_EMAIL("this.email"), 
    CONTACT_PHONE_NUMBER("this.phoneNumber"), 
    CONTACT_ORDER("this.order"), 

    private final String alias; 

    ContactCriteria(final String alias) { 
     this.alias = alias; 
    } 

    public String getAlias() { 
     return this.alias; 
    } 

    public static ContactCriteria fromString(final String name) { 
     ContactCriteria result = null; 

     if (name != null) { 
      result = Enum.valueOf(ContactCriteria.class, name); 
     } 
     return result; 
    } 

    public ContactCriteria returnThis() { 
     return this; 
    } 

} 

Наконец, я ищу для создания интерфейса для первого класса, чтобы принять метод fromString Т. Я предполагаю, что это должно быть похоже на:

public interface CriteriaInterface<T> { 
    static T fromString(String name); 
    //^This static is important 
} 

Я не нашел ни одного сообщения или стратегии для создания чего-то подобного с Enum. Я знаю, что Enum может реализовать интерфейс, но не знаю, как его получить.

Пожалуйста, помогите. Заранее спасибо

ответ

0

Если статический метод на вашем CriteriaInterface, не следует делать

CriteriaIntervace.fromString("") 

поскольку статические методы принадлежат к классу (в данном случае CriteriaIntervace) вместо того, чтобы объект?

0

Вы не можете ставить статические методы в интерфейсе, дженерики и т. Д. Не имеют прямого отношения к этому. Интерфейсы определяют методы экземпляра объекта, статические методы не являются частью интерфейса экземпляра, они являются частью интерфейса класса.

Самой простая работа вокруг будет обеспечить фабричный объект к GenericCriteriaValidator или сделать его абстрактным и обеспечивает:

abstract T getEnum(String name); 

Каждой реализация может затем реализовать getEnum для перечисления он использует.

0

Ну, вообще говоря, общий тип стирается, и у вас нет другого шанса, чем прямо указывать GenericCriteriaValidator, какую логику проверки он должен применять. Возможно, вам захочется отвлечь получение какого-либо типа и использовать фабричный шаблон , что позволит вам определить интерфейс для метода fromString.

Это привело бы к чему-то вроде этого:

public interface CriteriaInterface<T> { 
    static class Factory<U> { 
    U fromString(String name); 
    } 
} 

Однако, я не совсем понимаю, в пользу того, что в вашем примере. Просто требуется экземпляр CriteriaInterface<T> в качестве аргумента конструктора для вашего GenericCriteriaValidator и определите какой-то метод validate в этом интерфейсе.

Однако, если вы действительно хотите этого избежать, есть решение. Можно прочитать общий тип суперкласса какого-либо другого класса (это довольно хаки, требуется отражение, и я бы не рекомендовал его, но некоторым библиотекам нравится этот подход).Это требует, чтобы вы всегда объявить анонимный подкласс при использовании обобщенного класса:

class GenericCriteriaValidator<T extends Enum<?>> implements CriterionVisitor { 

    private final Method criteria; 

    public GenericCriteriaValidator() { 
    ParameterizedType parameterizedType = (ParameterizedType) getClass() 
     .getGenericSuperclass(); 
    try { 
     criteria = ((Class<?>) parameterizedType.getActualTypeArguments()[0]) 
      .getMethod("fromString", String.class); 
     criteria.setAccessible(true); 
    } catch (NoSuchMethodException e) { 
     throw new IllegalArgumentException(e); 
    } 
    } 

    @SuppressWarning("unchecked") 
    private CriteriaInterface<?> invokeFromString(String value) { 
    try { 
     return (CriteriaInterface<?>) criteria.invoke(null, value); 
    } catch (IllegalAccessException e) { 
     throw new IllegalStateException(e); 
    } catch (InvocationTargetException e) { 
     throw new IllegalArgumentException(e); 
    } 
    } 

    // Your other code goes here. 

} 

Имейте в виду, что вам нужно создать экземпляр GenericCriteriaValidator в качестве анонимного подкласса:

new GenericCriteriaValidator<ContactCriteria>() { }; // mind the braces! 

Как я уже сказал. Я не считаю это интуитивным, и это, безусловно, не «путь на Java», но вы все равно можете его рассмотреть.

1

Вы должны начать с того, что статический метод не допускается в интерфейсе Java.

Концепция интерфейсов сильно не согласуется со статическими элементами, поскольку они относятся к классу, а не к объекту.

Итак, если у вас есть статический метод в перечислении, это просто контейнер, которому назначено, но вы не должны его связывать никакими другими отношениями.

Что плохого здесь в дизайне, вы пытаетесь использовать перечисление для чего-то, что не посвящено, так как вы не должны этого, потому что вы так много боретесь.

Вопрос в том, enum instance является CriteriaInterface, то почему он должен предоставлять его по имени.

Enum содержит определение «константы», которое может представлять собой интерфейс, но не может быть общим. Вот почему enum может реализовать интерфейс.

Чтобы выразить, что вы можете определить интерфейс

interface Messanger { 
    String getMessage(); 
} 

И попытаться применить его к ENUM

enum Messages { 
INFO 
WARNING; 
} 

У вас есть два варианта,

Во-первых, создать поле, которое будет

enum Messages implements Messanger { 
    INFO, 
    WARNING; 

    private String message; 

    @Override 
    public String getMessage() { 
      return message; 
    } 
} 

Затем вы должны добавить конструктор для установки поля

enum Messages implements Messanger { 
    INFO("Info"), //We create an instance of class as we call the constructor 
    WARNING("Warnig") //We create an instance of class as we call the constructor 
    ; 

    private final String message; 

    public Message(String message) { 
     this.messsage = message; 
    } 

    @Override 
    public String getMessage() { 
      return message; 
    } 
} 

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

+0

Я не люблю этот дизайн, но нам предоставили рамки с некоторыми сторонними библиотеками, и мы просто пытаемся улучшить то, что у нас есть. Перечисления уже имеют этот статический метод, поэтому мы можем создать что-то сверху, но есть вещи, которые мы не можем изменить. Я изучаю этот вариант, а также другие. – madtyn

Смежные вопросы