2016-10-25 4 views
2

Я медленно изучаю новые функции Java 8, и я пытаюсь найти способ обработки иерархии классов (от дочернего к родительскому) в виде потока.Java8 Потоковая передача иерархии классов

Например, найдите аннотацию к классу или его родителям.

Перед Java 8, я бы сделал это так:

public static <T extends Annotation> T getAnnonationOn(Class<?> type, Class<T> annType) { 
    Class<?> t = type; 
    T annot = null; 
    while (t != null && annot == null) { 
     annot = t.getAnnotation(annType); 
     t = t.getSuperclass(); 
    } 
    return annot; 
} 

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

import java.lang.annotation.Annotation; 
import java.util.stream.Stream; 

public static <T extends Annotation> T getAnnonationOn(Class<?> type, Class<T> annType) { 
    return ClassIterator.streamSuperclass(type) 
     .map(t -> t.getAnnotation(annType)) 
     .filter(a -> a != null) 
     .findFirst() 
     .orElse(null); 
} 

public static class ClassIterator { 
    public static Stream<Class<?>> streamSuperclass(Class<?> type) { 
     if (type.getSuperclass() != null) { 
      return Stream.concat(Stream.of(type), Stream.of(type.getSuperclass()).flatMap(ClassIterator::streamSuperclass)); 
     } 
     return Stream.of(type); 
    } 
} 

Но я не вполне удовлетворен решением. Хотя я не сравнивал это, я думаю, что потоковая конкатенация довольно громоздка и исполнена.

Есть ли лучший способ превратить рекурсию в поток?

+1

По сути, вы ищете 'takeWhile' (перебираете суперклассы и берете, пока это не пусто), см. http: // stackoverflow. ком/вопросы/20746429/предел-а-поток -by-a-predicate – Tunaki

+1

'takeWhile()', входящий в Java 9. Также 3-arg версия 'Stream.iterate()', которая может моделировать циклы 'for'. –

ответ

3

В Java 9, вы бы, вероятно, использовать

public static Stream<Class<?>> streamSuperclass(Class<?> type) { 
    return Stream.iterate(type, Objects::nonNull, Class::getSuperclass); 
} 

но в Java 8, эта функция не доступна, так что вы можете прибегнуть к реализации потока вручную:

public static Stream<Class<?>> streamSuperclass(Class<?> type) { 
    return StreamSupport.stream(
     new Spliterators.AbstractSpliterator<Class<?>>(100L, 
      Spliterator.ORDERED|Spliterator.IMMUTABLE|Spliterator.NONNULL) { 
      Class<?> current = type; 
      public boolean tryAdvance(Consumer<? super Class<?>> action) { 
       if(current == null) return false; 
       action.accept(current); 
       current = current.getSuperclass(); 
       return true; 
      } 
     }, false); 
} 

Обратите внимание, что это будет перетекать с определенного типа на java.lang.Object. Если вы хотите, чтобы заказ был от Object до наиболее конкретного, нет никакого способа собрать элементы сначала, будь то рекурсивные или итеративные, не так важно, но Stream.concat действительно наименее эффективный вариант. Вы можете просто использовать

public static Stream<Class<?>> streamSuperclass(Class<?> type) { 
    return reverse(Stream.<Class<?>>builder(), type, Class::getSuperclass).build(); 
} 
private static <T> Stream.Builder<T> reverse(
     Stream.Builder<T> builder, T t, UnaryOperator<T> op) { 
    return t==null? builder: reverse(builder, op.apply(t), op).add(t); 
} 

итерационный вариант тоже не так уж плохо:

public static Stream<Class<?>> streamSuperclass(Class<?> type) { 
    List<Class<?>> l=new ArrayList<>(); 
    for(; type!=null; type=type.getSuperclass()) l.add(type); 
    Collections.reverse(l); 
    return l.stream(); 
} 

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

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