2016-03-16 4 views
8

У меня есть два метода, как следующийметод принять один объект класса или другой объект класса

private List<Long> getIds(String name, List<Cat> cats) { 
    List<Long> catIds = new ArrayList<>(); 
    for (Cat cat : cats) { 
     if (cat.getName().equals(name)) catIds.add(cat.getId()); 
    } 
    return catIds; 
} 

private List<Long> getIds(String name, List<Dog> dogs) { 
    List<Long> dogIds = new ArrayList<>(); 
    for (Dog dog : dogs) { 
     if (dog.getName().equals(name)) dogIds.add(dog.getId()); 
    } 
    return dogIds; 
} 

Мой кот и собака класса являются

public class Cat { 
    String name; 
    Long id; 
    // additional variables 
    // getters and setters 
} 

public class Dog { 
    String name; 
    Long id; 
    // additional variables 
    // getters and setters 
} 

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

Я попробовал следующее

private List<Long> getIds(String name, List<T> objects) { 
    List<Long> ids = new ArrayList<>(); 
    for (T object : objects) { 
     if (object.getName().equals(name)) ids.add(object.getId()); 
    } 
    return ids; 
} 

Но это не работает, как он жалуется, что общий T не имеет getName или getId

Здесь Cat и Dog имеют встроенные классы Java. В результате I НЕ МОГУТ выполнить наследование и предоставить суперкласс Animal для них с name и id в качестве переменных данных.

Есть ли способ, которым я мог бы слить два вышеуказанных метода без реализации наследования?

+2

make Animal interface содержащий getName. реализуйте это в Cat and Dog. в вашем методе getIds, измените? extends Animal –

+0

Вы также можете просмотреть этот [рецепт для утиной печати на Java] (https://en.wikipedia.org/wiki/Duck_typing#In_Java) из Википедии ... –

ответ

1

Хорошо, у меня есть, что вы не можете использовать любой тип наследования. Вот краткое решение с Reflection API, то:

private static List<Long> getIds(String name, List<?> objects) { 
    List<Long> ids = new ArrayList<>(); 
    for (Object object : objects) { 
     try { 
      Method getName = null; 
      Method getId = null; 

      for (Method method : object.getClass().getMethods()) { 
       if (method.getName().equals("getName") && method.getReturnType().equals(String.class) && method.getParameterTypes().length == 0) { 
        getName = method; 
       } 
       if (method.getName().equals("getId") && method.getReturnType().equals(Long.class) && method.getParameterTypes().length == 0) { 
        getId = method; 
       } 
       if (getName != null && getId != null && getName.invoke(object).equals(name)) { 
        ids.add((Long) getId.invoke(object)); 
        break; 
       } 
      } 
     } catch (Exception e) { 
      System.out.println(e); 
     } 
    } 
    return ids; 
} 
+2

Некоторые предложения: используйте 'equals' для сравнения строк и поднимите' IllegalArgumentException', если класс не имеет этого атрибута. Кроме того, 'List ' слишком строг; используйте 'Список '. –

+0

спасибо за равных - моя ошибка, написала этот код слишком быстро) – Cootri

+0

Кроме того, можете ли вы привести мне пример, где у любого экземпляра в списке входных объектов не будет доступного метода equals? Звучит интересно – Cootri

0

Вы должны использовать Reflection API, если вы не можете изменить классы Cat и Dog. В противном случае используйте интерфейс Animal (или тип отливку)

+0

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

+0

ok, код добавлен - см. Мой последний ответ :) – Cootri

1

Вы должны определить общий интерфейс для Cat и Dog, например Animal.

Чем вы можете передать List<Animal> в качестве параметра.

+0

Op говорит: «Здесь Cat and Dog - встроенные классы Java. В результате я НЕ МОЖЕТ выполнять наследование и предоставлять суперкласс Animal для них с именем и id в качестве переменных данных. ' – VinayVeluri

+0

Я редактирую свой ответ, чтобы показать вам еще один возможный подход. –

+0

@VinayVeluri ах .. Я пропустил, чтобы увидеть, что –

1

Можете ли вы просто создать интерфейс для собак и кошек?

interface animal { 
    ... 
    Long getId(); 
} 

public class Cat implements Animal 
public class Dog implements Animal 

тогда ваш метод станет

private List<Long> getIds(String name, List<Animal> animals) { 
    List<Long> ids = new ArrayList<>(); 
    for (Animal animal : animals) { 
     ids.add(animal.getId()); 
    } 
    return ids; 
} 

Относительно встроенный класс вы упомянули, вы можете обернуть их в другой класс и реализует интерфейс (или суперкласс), описывающую желаемое поведение.

Я однажды должен был предоставить тот же интерфейс для log4j и logback Событие Объект, они два совершенно разные.

здесь обертка идет ..

public abstract class EventWrapper { 
    public abstract String getFormattedMessage(); 
} 

И класс ребенка, который на самом деле держит в построенном классе

public class Log4jEvent extends EventWrapper { 
    LoggingEvent event; 

    public Log4jEvent(LoggingEvent event) { 
     this.event = event; 
    } 

    @Override 
    public String getFormattedMessage() { 
     if (event.getMessage() != null) { 
      return event.getMessage().toString(); 
     } else { 
      return null; 
     } 
    } 
} 

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

+0

Я не могу этого сделать. Я не могу редактировать классы Cat или Dog –

+1

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

+0

@ 이승진 Интересно. создавая новые классы, расширяющие классы Cat и Dog и реализуя интерфейс Animal? –

0

Простое решение, о котором я могу думать (сохранение наследования в стороне), проверяет, является ли объект типа Dog или Cat.Ниже приведен код для метода: -

private List getIds(String name, List objects) { 
    List<Long> ids = new ArrayList<>(); 

    for (Object obj : objects) { 
     if (obj instanceof Dog) { 
      Dog d = (Dog) obj; 
      if (d.getName().equals(name)) { 
       ids.add(d.getId()); 
      } 
     } else if (obj instanceof Cat) { 
      Cat c = (Cat) obj; 
      if (c.getName().equals(name)) { 
       ids.add(c.getId()); 
      } 
     } 
    } 
    return ids; 
} 

Однако он не использует дженерики.

+0

Нет Нет Нет. Это означало бы, что я могу перейти в список . поражает цель генериков –

+1

Это не уменьшает дублирование кода вообще. Вы использовали только два метода по 7 строк для одного метода из 18 строк. –

+0

@BalajiKrishnan Я не думаю, что есть еще одна более простая альтернатива. Лучше всего держать эти методы отдельно. –

0

Если это не проблема с несколькими линиями внутри метода. Как насчет этого?

private List<Long> getIds(String name, List<?> objects) { 
    List<Long> ids = new ArrayList<Long>(); 

    for (Object object : objects) { 
     if(object instanceof Dog){ 
      Dog dog = (Dog) object; 
      if (dog.getName().equals(name)) 
       ids.add(dog.getId()); 
     } else if (object instanceof Cat){ 
      Cat cat = (Cat) object; 
      if (cat.getName().equals(name)) 
       ids.add(cat.getId()); 
     } 
    } 
    return ids; 
} 

Более общий подход будет таким:

private <T> List<Long> getIds(String name, List<T> objects, IdGetter<T> idGetter) { 
    List<Long> ids = new ArrayList<Long>(); 
    for (T object : objects) { 
     Long id= idGetter.getId(object); 
     if(id != null){ 
      ids.add(id); 
     } 
    } 
    return ids; 
} 

public interface IdGetter<T> { 
    Long getId(T object); 
} 
+0

, хотя он выполняет свою работу, это открывает функцию, чтобы передать что-либо вроде List - не поистине generics –

+0

Да, но это не даст ничего плохого для других типов. Вы можете добавить возвращаемое значение null или исключение в случае неожиданных типов. – Alexander

+0

Неправильно технически, но как насчет сложности времени. Вы в конечном итоге итерируете всю коллекцию полностью, чтобы вернуть пустой список. –

4

Другая возможность: Если вы используете Java 8, можно передать в способах получения для имени и ID в качестве дополнительных параметров в методе. Это безопасно для типов и не требует отражения или изменений в исходных классах.

private <T> List<Long> getIds(String name, List<T> objects, Function<T, String> getName, Function<T, Long> getId) { 
    List<Long> ids = new ArrayList<>(); 
    for (T object : objects) { 
     if (getName.apply(object).equals(name)) { 
      ids.add(getId.apply(object)); 
     } 
    } 
    return ids; 
} 

Или короче, используя потоки:

private <T> List<Long> getIds(String name, List<T> objects, Function<T, String> getName, Function<T, Long> getId) { 
    return objects.stream().filter(o -> getName.apply(o).equals(name)) 
      .map(getId).collect(Collectors.toList()); 
} 

Использование:

List<Long> ids = getIds("some name", listOfCats, Cat::getName, Cat::getId); 

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

private List<Long> getIds(String name, List<?> objects) { 
    if (objects.size() == 0) { 
     return Collections.emptyList(); 
    } 
    if (objects.get(0) instanceof Cat) { 
     return getIds(name, (List<Cat>) objects, Cat::getName, Cat::getId); 
    } 
    if (objects.get(0) instanceof Dog) { 
     ... 
    } 
    throw new IllegalArgumentException("List containing unsupported type!"); 
} 
+0

Это был очень хороший подход. Но мой текущий проект должен быть в Java 7 . И клиент хочет, чтобы это была Java 7. :-) –

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