2015-12-11 6 views
2

Я создаю своего рода фреймворк, чтобы избежать повторения кода, и в определенный момент мне нужно преобразовать список объектов Foo в список объектов Bar.Java generics and streams

У меня есть объекты базы данных, которые расширяют

public class BaseEntity {...} 

и объекты презентации, которые расширяют

public class BaseDTO<T extends BaseEntity> {...} 

так

public class Foo extends BaseEntity {...} 

и

public class Bar extends BaseDTO<A extends BaseEntity> { 
    public Bar(Foo entity, Locale l) { 
     ... 
    } 
} 

Теперь, чтобы преобразовать список адвокатов в список Foo легко используя потоки

public abstract ClassThatUsesFooAndBar() { 

    public List<Bar> convertFooToBar(List<Foo> list) { 
     return list.stream().map(f -> new Bar(f, locale)).collect(...); 
    } 
} 

Но и здесь есть вопрос, эти Foo и Bar фактически непатентованные (А и В), поэтому класс который использует Foo и Bar, фактически равен ClassThatUsesAandB<A extends BaseEntity, B extends BaseDTO>, так что функция должна быть абстрактной и реализована как шаблонный код с правильными реализациями A и B, потому что, очевидно, вы не можете создавать типичные типы.

Есть ли способ использовать generics/streams/lambdas для создания функции, которая может быть записана один раз, чтобы классы реализации не нуждались в повторной реализации? Функция подписи будет

public List<B> convertAToB(List<A> list); 

Я надеюсь, что я был достаточно ясно, что мне нужно, если вам нужны дополнительные пояснения, пожалуйста, спросите

Спасибо!

+1

Пожалуйста, не используйте имена классов с одной буквой, так как их очень сложно отличить от параметров типа. Если вы застряли в случайных именах, вы можете использовать 'Foo',' Bar' или 'Baz'. – biziclop

+0

На самом деле я использовал A и B, потому что в конце обсуждения они фактически являются параметрами типа. Я попытаюсь перефразировать ... – Luca

+0

Можете ли вы уточнить использование дженериков? вы пишете A/B extends AnObject, но в вашем примере A/B не расширяет ничего – AdamSkywalker

ответ

3

Я думаю, что самый простой способ - использовать лямбда для преобразования.

public static <A,B> List<B> convertList(List<A> list, Function<A,B> itemConverter) { 
    return list.stream().map(f -> itemConverter.apply(f)).collect(...); 
} 

И тогда вы можете использовать его как это:

List<Bar> l = convertList(fooList,foo -> new Bar(foo.getBaz())); 

Или, если вы хотите, вы можете извлечь его в своем собственном имени класса:

public class Foo2BarConverter implements Function<Foo,Bar> { 
    @Override 
    public Bar apply(Foo f) { 
     return new Bar(f.getBaz()); 
    } 
} 

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

+0

что вы имеете в виду, тратя? вы предлагаете вернуть поток для последующей обработки вместо создания списка результатов? – AdamSkywalker

+0

@AdamSkywalker Да, это лучше подходит для функционального подхода. – biziclop

+0

Я понял. В любом случае, имя BaseDTO указывает, что результаты, скорее всего, будут сериализованы и отправлены где-то – AdamSkywalker

1

Самая сложная проблема с вашим вопросом на самом деле не в шаблоне или потоках, это дженерики. Попытка сделать new B немного беспорядок. Вы не можете сделать это напрямую, и любое обходное решение не слишком чистое.

Для шаблона, однако, вы можете сделать немного лучше благодаря методам по умолчанию Java 8 в интерфейсе. Рассмотрим следующий интерфейс:

public interface ConversionHandler<A,B> { 

    B constructB(A a, Locale locale); 

    default List<B> convertAToB(List<A> list, Locale locale) { 
    return list.stream().map(a -> constructB(a, locale)).collect(Collectors.toCollection(ArrayList::new)); 
    } 
} 

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

public class ClassThatUsesAandB<A, B> implements ConversionHandler<A,B> { 

    @Override 
    public B constructB(A a, Locale locale) { 
    return null; //This is tricky 
    } 
} 

Однако, если подкласс конкретно, это довольно просто

public class ConverterClass implements ConversionHandler<String,Integer> { 

    @Override 
    public Integer constructB(String s, Locale locale) { 
    return s.length(); 
    } 
} 

Так Followup вы можете найти хороший шаблон дизайна для создания строительства родовых объектов в ремонтопригодны и читаемом насколько это возможно.

+0

Да, я думал о том, чтобы делать что-то подобное, поэтому в подклассах я бы только необходимо сделать создание конкретного типа B. Я немного перефразировал вопрос, так как вы говорите, что Bs - все дженерики. Я подумаю об этом в выходные, но я думаю, что ваш ответ будет правильным в конце! Спасибо :) – Luca