2015-04-29 3 views
3

Я надеялся создать чистый код, который может рекурсивно выкапывать в коллекцию и печатать первое целое, которое он находит. Исходя из фона C++, этот подход выглядел так:Статический полиморфизм с дженериками в Java

class Test { 
static void print(Integer i) { 
    System.out.println(i); 
} 

static <T> void print(ArrayList<T> arr) { 
    T entry= arr.at(0); 
    Test.print (entry); 
} 

static void Test() { 
    ArrayList<ArrayList<Integer>> arrayOfArrayOfInt= create(); 
    print(arrayOfArrayOfInt); 
} 
} 

К сожалению, это не сработало.

Одним из альтернатив является отказ от статического полиморфизма и создание функции print (Object o), а затем выполнение ряда проверок instanceof для перехода в правильное поведение. Это единственный способ сделать это из-за стирания стилей Java или есть более элегантный подход?

+0

Можете ли вы уточнить, как он не работает? –

+0

Легче сфокусироваться на поведении полиморфизма, если вы можете вырезать посторонние ошибки ('get()' vs 'at()', отсутствующий 'create()', отсутствующие импорт) и опубликовать пример компиляции. Вот версия, которую я должен запустить; Я думаю, что это делает проблему отправки немного более ясной. https://gist.github.com/apjanke/aa8583e2f36c0955589c –

+0

Извините за неправильное имя метода. Я намеренно пропустил create(), потому что он добавляет много шума. Спасибо, что создали суть. –

ответ

2

Следующий метод будет рекурсивно рыть в Object и возвращать Optional, содержащий первый Integer он находит, или Optional.empty(), если он не может найти один.

static Optional<Integer> firstInt(Object o) { 
    if (o instanceof Integer) 
     return Optional.of((Integer) o); 
    if (o instanceof int[]) { 
     int[] array = (int[]) o; 
     return array.length > 0 ? Optional.of(array[0]) : Optional.empty(); 
    } 
    if (o instanceof Object[]) 
     return firstInt(Arrays.asList((Object[]) o)); 
    if (o instanceof Iterable) { 
     for (Object o2 : (Iterable<?>) o) { 
      Optional<Integer> result = firstInt(o2); 
      if (result.isPresent()) 
       return result; 
     } 
    } 
    return Optional.empty(); 
} 

You может сделать это с помощью полиморфизма, но вы должны создать интерфейс Searchable как этот

interface Searchable { 
    Optional<Integer> search(); 
} 

, а затем создать классы-оболочки для всех конкретных типов, которые вы хотите, чтобы иметь возможность поиск. Например:

public final class SearchableList<T extends Searchable> extends AbstractList<T> implements Searchable { 

    private final List<T> list; 

    SearchableList(List<T> list) { 
     this.list = list; 
    } 

    // rest omitted 
} 

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

+0

Если этот 'firstInt()' намеревается обрабатывать все случаи массива, в нем отсутствуют случаи для 'o instanceof Integer []' и 'o instanceof Number []' (потому что массивы не ковариантны с их типами элементов). Я бы придерживался только типов 'Collections'; смешивание коллекций и массивов - это боль. –

+1

@ AndrewJanke Массивы являются ковариантными. Попробуйте 'System.out.println (new Integer [3] instanceof Object []);' –

+0

Святая корова, вы правы. Как я ошибался? –

1

Короткий ответ: Да, отказаться от статического полиморфизма и использовать instanceof. Или просто напишите локальный цикл, чтобы выполнить свою работу.

Дженерики Java намного слабее, чем шаблоны C++. В частности, компилятор не будет создавать для вас новые специализации. Все, что вы можете сделать с ними, - это сделать более ограничительную статическую проверку типов на классах и методах, которые уже определены с помощью обычных средств (например, вы написали их вручную или сгенерировали их с помощью инструмента). Как вы подозревали, тип стирания имеет значение.

С Java дженериков, этот код:

static <T> void print(ArrayList<T> arr) { 
    T entry= arr.at(0); 
    Test.print (entry); 
} 

всегда эффективно превращается в к этому, из-за типа стирания:

static Object void print(ArrayList<Object> arr) { 
    Object entry= arr.at(0); 
    Test.print (entry); 
} 

Он не будет производить отдельные специализации в качестве шаблонов C++ будет.

Ваш конкретный фрагмент кода не работает, потому что ему не хватает нескольких конкретных методов, которые вам нужно будет предоставить вручную. Но я думаю, я могу видеть, куда ты пытаешься пойти. (Я предполагаю, что вы хотите получить код, который будет работать в более широком диапазоне типов, чем только List<List<Integer>>.) И вы не получите удовлетворительного решения для этого, используя Java-генераторы. (You может заставить его работать, но вам придется написать много кода вручную, какие шаблоны вам не помогут. И были бы какие-то жесткие кромки.) Переключитесь на проверку динамического типа и instanceof ; это способ Java для этого.

1

Прежде всего, функция получения элемента «get» не «at». Однако основная проблема заключается в том, что вы пытаетесь вызвать функцию «print», которая принимает целое число, но передавая ему значение типа «T».

Если вы собираетесь позвонить печать и передать ему значение неизвестного типа, функция должна либо принять аргумент типа Object или взять общий аргумент, такие как

public <T> void print(T item) 

или

public void print (Object item) 

Java будет отвечать только вызовам функций для наиболее конкретной перегрузки во время компиляции, если вы пытаетесь найти правильную перегрузку во время выполнения, вам придется использовать instanceof. Кроме того, вы можете создать HashMap, который указывает объекты класса на объекты Consumer.

HashMap<Class<?>, Consumer<?>> prints = new HashMap<>(); 
void setup() { 
    prints.put(ArrayList.class, (list) -> { 
     for (Object o : list) { 
      print(o); 
     } 
    } 

    prints.put(Integer.class, (integer) -> System.out.println(integer)); 
} 

void print(Object o) { 
    Consumer<?> func = prints.get(o.getClass()); 
    func.accept(o); 
} 
0

Если вы хотите сохранить этот код так, как он есть, вам придется добавить еще один метод печати для объектов.

public static void print(Object o){} 

Но в любом случае есть более подходящие подходы к вашему примеру.

2

Вы правы, предполагая, что вы не можете обрабатывать джоки Java, как если бы вы использовали шаблон C++.

Таким образом, без instanceof, это не догадаться, что запись является Integer (ни во время выполнения и не во время компиляции):

static <T> void print(ArrayList<T> arr) { 
    T entry= arr.get(0); 
    Test.print (entry); 
} 

так «, чтобы создать какой-то чистый код, который может рекурсивно копаться в сбор и печать первой Integer это найти» (или, вернее, первый не элемент списка с индексом 0) в Java:

static void print(Object obj) { 
    System.out.println(obj); 
} 

static <T> void print(List<T> arr) { 
    T entry = arr.get(0); 
    if (entry instanceof List) 
     print((List<?>) entry); 
    else 
     print(entry); 
} 

static void test() { 
    List<List<Integer>> arrayOfArrayOfInt = create(); 
    print(arrayOfArrayOfInt); 
} 

Не элегантное решение, которое вы хотели бы, но выполнимое решение обобщенного типа системы повторного п.л..

+0

Я думаю, что это объяснение немного не работает. Без 'instanceof' и casts, компилятор * делает * угадывает, что делать с этой параметризованной' print (ArrayList arr) 'entry: он рассматривает' T' как 'Object', пытается вызвать' Test.print (Object) ' , который не существует, и вы получаете ошибку компилятора. –

+0

Действительно. Я уточню это. Благодарю. –

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