2016-05-19 3 views
-1

Мне часто возникает соблазн написать код с использованием утилиты коллекции Guava и найти себе переписывание кода в простой Java 7 после просмотра фрагмента кода, потому что IMHO оказывается более кратким и простым для чтения с использованием простой старой Java.Guava Collection Utils vs. plain Java 7

Например положить список объектов на карте с уникальным индексом, взятый из объектов будет выглядеть примерно так, используя гуавы:

List<A> myList = ... 
Map<A, B> mappings = Maps.uniqueIndex(myList, new Function<A, B>() { 
    @Override 
    public CustomerFinance apply(final A input) { 
     return input.getB(); 
    } 
}); 

, тогда как в ванили Java 7 было бы:

List<A> myList = ... 
Map<A, B> mappings = new HashMap<>(myList.size()); 
for (A a : myList) { 
    mappings.put(a.getB(), a); 
} 

Это 5 LOC для отображения в Guava (даже не считая строки @Override) против 4 LOC в простой Java.

Почему я должен использовать утилиты Guava здесь? Каков был стимул для создания utils в первую очередь? Не хватает ли какой-то дополнительной выгоды, которую предоставит мне Гуава?

+1

Java 8 был удален более 2 лет, и он может выполнить задачу в 1 строке. Почему вы заботитесь о Java 7? – Bohemian

+1

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

+1

Ваша Java 7 только короче, потому что это неполный «перевод» кода Guava. Полученная карта Guavas является неизменной и не позволяет «null» как одно из значений. Ваш код полностью игнорирует это. – Tom

ответ

3

Я пропустил какую-то дополнительную выгоду, которую предоставит мне Гуава?

Да, это так.

Есть несколько особенностей в гуавах, которые Переведенный промахи кода (JavaDoc of Maps#uniqueIndex):

  • null значений запрещены (#)
  • null для ключей запрещаются (функциональная клавиша не должен возвращать null для заданного значения)
  • дублирующиеся ключи запрещены (вы не можете неожиданно перезаписать существующие ключи)
  • итоговая карта immu таблица

Если вы все это проверите, то ваш код должен быть длиннее версии Guavas.

(#) Учтите, что ваша переведенная версия в настоящее время запрещает также null, потому что вы вызываете #getB() на свой объект значения. Это вызовет NullPointerException, как в Гуаве. Если вы получите ключ из другого места, например, в mappings.put(generateArtificalKey(), a);, то null будет в порядке для версии Java 7.


Другим преимуществом является код чистки ключевой функции. Его можно легко переместить в собственный класс и повторно использовать. Примером этого может быть функция, которая извлекает идентификатор объектов базы данных для создания карты с этим. Но это скорее мнение, чем факт, поскольку ваш переведенный код (цикл for) также можно извлечь и использовать для других случаев.

+2

На самом деле, ваше утверждение о 'null' неверно: версия vanilla НЕ выбрасывает NPE, если' a.getB() 'возвращает' null', тогда как версия Guava будет. –

+0

@StefanHaberl Ваша версия будет вызывать NPE, потому что если 'a' является' null', то 'a.getB()' не будет работать. Если вы не будете называть что-то на 'a' и использовать что-то еще в качестве ключа, тогда _yes_ версия ванили не будет бросать NPE. Но я использовал вашу версию в качестве ссылки. Вот почему я переместил эту точку на дно (сначала у меня было это в списке). Надеюсь, теперь мой ответ немного яснее. – Tom

+1

Конечно, но если 'a! = Null' и' a.getB() == null', версия ванили будет работать, тогда как версия Guava будет бросать NPE: D –

2

Вы можете утверждать, что да, в плане LOC нет большого выигрыша. Однако стиль Guava более functional.

Функция преобразования может быть передана и использована/использована повторно.

Function<A,B> map = new Function<A, B>() { 
@Override 
public CustomerFinance apply(final A input) { 
    return input.getB(); 
} 

Теперь вы можете сдать карту и использовать ее в разных местах.

Map<A, B> mappings = Maps.uniqueIndex(myList,map); 
Map<C, D> mappings2 = Maps.uniqueIndex(myList,map); 

Но опять же, Guava ограничивается Java 1.6 конструктов, которые не дают лучший опыт для декларативного стиля.

Хорошая новость заключается в том, что Function в гуавы является functional interface, так что если вы переезжаете в Java 1.8, вы можете начать использовать лямбды с первого дня, и ваш код будет выглядеть следующим образом,

Map<A, B> mappings = Maps.uniqueIndex(myList, input-> input.getB()); 

Или

+0

Я знаю, что я мог бы использовать лямбда в Java 8. Однако для текущего проекта я придерживаюсь Java 7. Моя первоначальная проблема заключается в том, как далеко я должен пойти по «функциональной» дороге с Java 7.Если я не разделяю или повторно использую функцию, вы в основном говорите, что это вопрос вкуса (или просто более «модный» подход)? –

+0

@StefanHaberl Я не думаю, что в моем ответе есть что-то, что можно было бы считать «модным». Я показал вам несколько примеров, чтобы продемонстрировать преимущества и краткость. –

+0

Согласовано. Таким образом, преимущество в краткости - это когда я начинаю делиться своей функцией. Кого я не знаю. Переход на Java 8 (к сожалению) не является вариантом. Поэтому нет никакой пользы. –

1

Когда я программирую в версиях Java старше 8, я обычно создаю связанный с ним класс утилит, называемый сущностью, но во множественном числе, и добавляю к нему весь метод утилиты. Таким образом, я сохраняю свои сущности чистыми, и у меня есть доступ к большой панели полезных методов. Я не систематически создаю класс утилиты, но только тогда, когда это необходимо для снижения уровня шума.

public class MyEntity { 
    private String text; 
    public String getText() { return text; } 
    public void setText (int text) { this.text = text; } 
} 

public final class MyEntities { 
    private MyEntities() {} 

    private enum MyEntityToString implements Function<MyEntity,String> { 
    TEXT_GETTER { 
     @Override public String apply(MyEntity input) { return input.getText(); } 
    }; 
    } 
    public static Function<MyEntity,String> textGetter() { return MyEntityToString.TEXT_GETTER; } 
} 

Затем, использование становится таким же простым, как с помощью метода MyEntities.textGetter(), когда это необходимо. Если вы используете его только один раз, да, вы теряете с точки зрения LOC, но если вы используете его несколько раз, вы просто выигрываете. Кроме того, если у вас когда-либо была ошибка, вы должны ее исправить только один раз и не находите каждое использование, чтобы исправить ее повсюду.

+0

Я полностью согласен. DRY. Я делаю то же самое, когда мне нужен точный фрагмент кода дважды (в основном с Predicates, хотя). Но чаще всего у меня есть код, который относится только к одному варианту использования. И тогда я не хочу экзернализировать логику в отдельную функцию, которую IMHO труднее читать (опять же, когда я должен придерживаться Java 7) –

+1

Если это специфично для одного прецедента, вы можете написать функцию в другое место класса, использующее его. Это еще легче читать. Затем переместите его в класс утилиты, если появится больше случаев использования. –