[SonarLint]: сделать этот анонимный внутренний класс лямбдой

Приведенный ниже код работает, но у меня есть уведомление от SonarLint, потому что я использую анонимный класс в потоке вместо лямбда-выражения, и я не вижу, как улучшить приведенный ниже код, избегая уведомления:

Properties prop = new Properties();
Properties temp = new Properties();
//... add some values and keys in prop and temp

prop.putAll(temp.entrySet().stream()
    .filter( entry -> !prop.containsKey(entry.getKey()))
    .map( new Function<Entry<Object, Object>, Entry<String, String>>(){ 
        @Override
        public Entry<String, String> apply(Entry<Object, Object> entry) {
            return new Entry<String, String>() {
                @Override
                public String setValue(String value) {
                    return value.trim().toLowerCase();
                }

                @Override
                public String getValue() {
                    return ((String) entry.getValue()).trim().toLowerCase();
                }

                @Override
                public String getKey() {
                    return ((String) entry.getKey()).trim().toLowerCase();
                }
            };
        }
    })
    .collect(Collectors.toMap(Entry<String,String>::getKey, Entry<String,String>::getValue)));

Пояснение кода: я использую класс свойств из java.util, и, к сожалению, entrySet свойств возвращает Entry<Object, Object>, а не Entry<String, String>. Я хочу «присоединить» два объекта свойств, помещая ключ и значение в нижний регистр. Итак, карта позволяет конвертировать Entry<Object, Object> в Entry<String,String>. Вот почему существует анонимный класс.


person Nana Ba    schedule 25.07.2017    source источник


Ответы (1)


Сонар предлагает заменить

prop.putAll(temp.entrySet().stream()
    .filter( entry -> !prop.containsKey(entry.getKey()))
    .map( new Function<Entry<Object, Object>, Entry<String, String>>(){ 
        @Override
        public Entry<String, String> apply(Entry<Object, Object> entry) {
            return new Entry<String, String>() {
                @Override
                public String setValue(String value) {
                    return value.trim().toLowerCase();
                }

                @Override
                public String getValue() {
                    return ((String) entry.getValue()).trim().toLowerCase();
                }

                @Override
                public String getKey() {
                    return ((String) entry.getKey()).trim().toLowerCase();
                }
            };
        }
    })
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue)));

(Я удалил ненужные аргументы типа в сборщике)

с

prop.putAll(temp.entrySet().stream()
    .filter( entry -> !prop.containsKey(entry.getKey()))
    .map(entry -> new Entry<String, String>() { 
        @Override
        public String setValue(String value) {
            return value.trim().toLowerCase();
        }

        @Override
        public String getValue() {
            return ((String) entry.getValue()).trim().toLowerCase();
        }

        @Override
        public String getKey() {
            return ((String) entry.getKey()).trim().toLowerCase();
        }
    })
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue)));

который использует лямбда-выражение в качестве замены для анонимного внутреннего класса, реализующего Function, а не для реализации Entry.

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

prop.putAll(temp.entrySet().stream()
    .filter( entry -> !prop.containsKey(entry.getKey()))
    .map(entry -> new AbstractMap.SimpleImmutableEntry<>(
        ((String) entry.getKey()).trim().toLowerCase(),
        ((String) entry.getValue()).trim().toLowerCase()))
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue)));

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

prop.putAll(temp.entrySet().stream()
    .filter( entry -> !prop.containsKey(entry.getKey()))
    .collect(Collectors.toMap(
        entry -> ((String) entry.getKey())  .trim().toLowerCase(),
        entry -> ((String) entry.getValue()).trim().toLowerCase())));
person Holger    schedule 25.07.2017