Самый простой способ создать MutableGuiceKeyToInstanceMap?

#java #map #guice #guava

#java #словарь #графический интерфейс #гуава

Вопрос:

Я хотел бы найти или реализовать, MutableGuiceKeyToInstanceMap работающий точно так же, как com.google.common.collect.MutableClassToInstanceMap из Guava, но использующий com.google.inject.Key<T> из Guice вместо Class<T> .

Я не смог найти это в Guice, и я не могу реализовать это так, как MutableClassToInstanceMap было реализовано из-за того, что его суперкласс ConstrainedMap является закрытым для пакетов. Я не могу использовать MapConstraint.constrainedMap ни то, ни другое, поскольку у меня не было бы возможности добавить методы getInstance и putInstance (а без них все это совершенно бессмысленно).

Создание собственной копии ConstrainedMap класса вынудило бы меня скопировать довольно много дополнительных классов, так что это не тот путь. Создание вспомогательной карты через MapConstraint.constrainedMap и создание MutableGuiceKeyToInstanceMap extends ForwardingMap , которое делегирует все помощнику, могло бы сработать, но это все еще довольно громоздко. Есть идея получше?

Как вы думаете, предложение сделать ConstrainedMap общедоступным — хорошая идея?

Ответ №1:

Некоторые мысли:

  1. Мне любопытно, почему вы хотите это сделать.
  2. A ForwardingMap кажется подходящим для этого. Что в этом громоздкого?
  3. Делать ConstrainedMap общедоступным было бы не очень хорошей идеей.

Комментарии:

1. 1. Для реализации пользовательской области. Хорошо, на самом деле это не нужно, поскольку нет никого, кто мог бы вставить туда что-нибудь неправильное. 2. Таким образом, я определяю два почти идентичных класса — один из них просто для того, чтобы было на что пересылать. Но это не так плохо, как я думал. 3. Почему бы и нет?

2. 1. На самом деле мне нужны только два метода, getInstance и putInstance ни один из методов Map не требуется. Тем не менее, я хотел создать карту (понятия не имею, как вызвать такую «карту», в которой отсутствуют все методы Map). 2. На самом деле это действительно просто. 3. Я вижу, что для поддержания ограничения оно должно быть либо закрытым для пакета, либо все методы, изменяющие и раскрывающие мутатор (например entrySet() ), должны быть окончательными.

Ответ №2:

Я не понимаю, почему вам не нравится сочетание ForwardingMap и MapConstraint.constrainedMap . Код довольно прост и выглядит почти точно так же, как если бы вы расширяли ConstrainedMap напрямую:

 import com.google.common.collect.ForwardingMap;
import com.google.common.collect.MapConstraint;
import com.google.common.collect.MapConstraints;
import com.google.inject.Key;
import org.apache.commons.lang.NotImplementedException;

import java.util.HashMap;
import java.util.Map;


public final class MutableGuiceKeyToInstanceMap<B>
        extends ForwardingMap<Key<? extends B>, B> {

    /**
     * Returns a new {@code MutableGuiceKeyToInstanceMap} instance backed by a {@link
     * java.util.HashMap} using the default initial capacity and load factor.
     */
    public static <B> MutableGuiceKeyToInstanceMap<B> create() {
        return new MutableGuiceKeyToInstanceMap<B>(new HashMap<Key<? extends B>, B>());
    }

    /**
     * Returns a new {@code MutableGuiceKeyToInstanceMap} instance backed by a given
     * empty {@code backingMap}. The caller surrenders control of the backing map,
     * and thus should not allow any direct references to it to remain accessible.
     */
    public static <B> MutableGuiceKeyToInstanceMap<B> create(Map<Key<? extends B>, B> backingMap) {
        return new MutableGuiceKeyToInstanceMap<B>(backingMap);
    }


    private final Map<Key<? extends B>, B> delegate;

    private MutableGuiceKeyToInstanceMap(Map<Key<? extends B>, B> delegate) {
        this.delegate = MapConstraints.constrainedMap(delegate, VALUE_MATCHES_GUICE_KEY);
    }

    @Override
    protected Map<Key<? extends B>, B> delegate() {
        return delegate;
    }

    private static final MapConstraint<Key<?>, Object> VALUE_MATCHES_GUICE_KEY = new MapConstraint<Key<?>, Object>() {
        @Override
        public void checkKeyValue(Key<?> key, Object value) {
            matchesGuiceKey(key, value);
        }
    };

    public <T extends B> T putInstance(Key<T> key, T value) {
        return matchesGuiceKey(key, put(key, value));
    }

    public <T extends B> T getInstance(Key<T> key) {
        return matchesGuiceKey(key, get(key));
    }

    private static <B, T extends B> T matchesGuiceKey(Key<T> key, B value) {
        throw new NotImplementedException("TODO");
    }

    private static final long serialVersionUID = 0;
}
  

Код очень похож на MutableClassToInstanceMap , и нет необходимости расширять ForwardingMap … Конечно, вам нужно добавить delegate() метод и сопровождающее его поле, но остальное идентично.

Я опустил matchesGuiceKey() реализацию в качестве упражнения для читателя. Удачи! Вам может это понадобиться.

Комментарии:

1. Хороший ответ, это действительно не так уж плохо. Метод matchesGuiceKey может быть забавным упражнением, спасибо также за ссылки.

2. @eneveu: Я не думаю, что я правильно выполнил упражнение, поскольку я использую там @SuppressWarnings("unchecked") final Class<T> rawClass = (Class<T>) key.getTypeLiteral().getRawType(); . Кажется, это работает, но есть идея получше?

3. На самом деле, реализовать matchesGuiceKey метод непросто. Я иронизировал в своем ответе. Ваш вопрос был сосредоточен на невозможности расширения ConstrainedMap , и я ответил на эту часть вопроса. Но matchesGuiceKey материал на самом деле сложнее. Я попытался реализовать этот метод, но был час ночи, и я не смог заставить его работать. Я мог бы попробовать еще раз позже сегодня, но дженерики не сделают это легким 😉 И даже не заставляй меня рассказывать обо всей TypeLiteral банке с червями… Если только мы не найдем специалиста по дженерикам?

4. @eneveu: Я реализовал это здесь . Это может быть неправильно, но я так не думаю. Результат метода TypeLiteral.getRawType() требует непроверенного приведения, но это ИМХО нормально, поскольку он возвращает что-то вроде raw Map для типа literal like Map<K, ? extends E super S> и нет способа выразить их взаимосвязь в системе Java generics.

5. @eneveu: Остается только одна проблема: что-то вроде m.put(stringListKey, integerList); не должно быть возможным, но — из-за стирания — это возможно, и нет никакого способа обойти это, кроме запрета всех других мутаторов putInstance .