2017-02-19 23 views
1

Моя цель - перенести метод getAllFields в init, записав его как анонимную функцию, я знаю, что это вполне возможно, используя функциональное программирование.Java 8, анонимный рекурсивный вложенный метод

public void init(){ 

} 

public static Field[] getAllFields(Class klass) { 
    List<Field> fields = new ArrayList<Field>(); 
    fields.addAll(Arrays.asList(klass.getDeclaredFields())); 
    if (klass.getSuperclass() != null) { 
     fields.addAll(Arrays.asList(getAllFields(klass.getSuperclass()))); 
    } 
    return fields.toArray(new Field[] {}); 
} 

Я пробовал использовать функцию, а также BiFunction, но потерял abit. Может ли кто-нибудь дать фрагмент о том, как реализовать такой случай?

+4

Какая цель? Obfuscate ваш код? Есть много случаев, когда использование лямбда полезно, но это действительно не один из них. –

+0

Пожалуйста, покажите, что вы пробовали, и объясните, с какими проблемами вы столкнулись. – shmosel

+0

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

ответ

1

Эта реализация getAllFields ужасно неэффективна, создавая несколько экземпляров и массивов ArrayList, многократно копируя все данные между ними взад и вперед. К счастью, иерархии классов редко настолько глубоки, что это становится узким местом.

Тем не менее, вы можете реализовать это с прямой передней петли, которая является более простым и более эффективным:

public static Field[] getAllFields(Class<?> klass) { 
    List<Field> fields = new ArrayList<>(); 
    for(; klass!=null; klass=klass.getSuperclass()) 
     Collections.addAll(fields, klass.getDeclaredFields()); 
    return fields.toArray(new Field[0]); 
} 

Существует не малейшей выгоды от использования рекурсии здесь.

С петлей, вы можете легко создать Function, если вы действительно хотите:

public void init(){ 
    Function<Class<?>,Field[]> f = klass -> { 
     List<Field> fields = new ArrayList<>(); 
     for(; klass!=null; klass=klass.getSuperclass()) 
      Collections.addAll(fields, klass.getDeclaredFields()); 
     return fields.toArray(new Field[0]); 
    }; 
    Field[] someFields = f.apply(SomeClass.class); 
} 

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

С Прямодушными петлями, вы можете просто написать

public void init(){ 
    List<Field> fields = new ArrayList<>(); 
    for(Class<?> klass=SomeClass.class; klass!=null; klass=klass.getSuperclass()) 
     Collections.addAll(fields, klass.getDeclaredFields()); 
    Field[] someFields = fields.toArray(new Field[0]); 
} 

, хотя, на самом деле, редко существует реальная причина для копирования содержимого fields в массив, вы могли бы просто работать с List<Field> вместо ,

Таким образом, инкапсуляция цикла в именованный метод, описывающий его назначение, например getAllFields, на самом деле является хорошей вещью. Если вы не хотите его раскрывать, объявите его private вместо public.

+0

Индуктивный или рекурсивный подход к этому вопросу был не темой, хотя, конечно, вы совершенно правы в том, чтобы ничего не добиваться с рекурсивным подходом, и я полностью согласен с тем, что индуктивный путь гораздо более оправдан, чем rec 1, меня больше интересовала методология функционального программирования, как и в javaScript, что позволяет создать анонимный метод, который не будет создавать ссылку и не создаст ссылку на метод, просто получит значение для ссылка на локальный метод, надеялась увидеть это в java 8. – Benma

+0

Лямбда-выражение похоже на аноним mous method (на самом деле они скомпилированы для метода), но он не может называть себя рекурсивно. Ну, для синтаксиса вызова метода требуется имя метода ... – Holger

2

Это еще не возможно. В Java 9, Stream класс будет иметь iterate метод, позволяющий реализовать это следующим образом:

Field[] allFields = Stream 
    .iterate((klass, Objects::nonNull, Class::getSuperclass) 
    .flatMap(c -> Stream.of(c.getDeclaredFields())) 
    .toArray(Field[]::new); 

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