Java - Mapa de tipo genérico para o consumidor da referida tipo

Gikkman:

Corri para este problema recentemente, quando eu estava brincando. Basicamente, eu queria fazer uma classe central onde os consumidores de um determinado tipo poderia registrar-se. Algo em seguida, publica objetos para os consumidores, e apenas aqueles que consomem o tipo publicada deve recebê-lo.

Todo o programa pode ser resumido a isto:

public class Main
{
    public interface Fruit
    {
    }

    public class Apple implements Fruit
    {
    }

    public class Banana implements Fruit
    {
    }

    public interface FruitConsumer<T extends Fruit>
    {
        public void consume(T fruit);
    }

    public class Consumers<T extends Fruit>
    {
        Map<Class<T>, Collection<FruitConsumer<T>>> consumers = new HashMap<>();

        public void register(Class<T> clazz, FruitConsumer<T> consumer)
        {
            consumers.putIfAbsent(clazz, new HashSet<>());
            consumers.get(clazz).add(consumer);
        }

        public void consume(T fruit)
        {
            Collection<FruitConsumer<T>> cons = consumers.get(fruit.getClass());
            for (FruitConsumer<T> c : cons)
            {
                c.consume(fruit); // <-- Breaks if T is Apple
            }
        }
    }

    public class BananaConsumer implements FruitConsumer<Banana>
    {
        void register(Consumers c)
        {
            c.register(Banana.class, this);
            c.register(Apple.class, this); // <-- Why does this work?
        }

        @Override
        public void consume(Banana banana)
        {
            System.out.println("Mmm, banana");
        }
    }

    public static void main(String... args)
    {
        Main main = new Main();
        main.run();
    }

    private void run()
    {
        Consumers consumers = new Consumers<>();
        BananaConsumer bananaConsumer = new BananaConsumer();

        bananaConsumer.register(consumers);

        Banana banana = new Banana();
        consumers.consume(banana);

        Apple apple = new Apple();
        consumers.consume(apple);
    }
}

Agora, eu acho que entendo por que essa falha. Não há nenhuma maneira para que o compilador sabe que a Tdo Consumers.registermétodo é o mesmo T, para ambos os parâmetros. O compilador só pode impor que ambos os argumentos atende ao requisito T extends Fruit. Eu quero lembrar de ser capaz de usar uma estrutura de código semelhante em C ++, então tem que haver algo que os dois idiomas faz diferente. É a minha suposição aqui correto?

Além disso, qual seria a maneira correta de ir sobre isso? Gostaria diferentes consumidores de subtipos de frutas para ser capaz de registrar-se, e receber apenas os frutos eles se alistar como consumidores de. Além disso, eu gostaria de um pouco de segurança no que você pode registar-se como um consumidor off (eu notei que este código funciona, desde que ninguém registra-se "errado").

Por último, qual é o nome desse fenômeno? Basicamente, o que eu google para saber mais sobre isso.

OldCurmudgeon:

Eu acho que você tomou os genéricos um pouco longe demais.

Seu Consumersobjeto não precisa ser genérica, apenas o Mapque detém. Você nunca vai precisar de um Consumers<Banana>, por exemplo?

Tente isto:

public interface Fruit {
}

public class Apple implements Fruit {
}

public class Banana implements Fruit {
}

public interface FruitConsumer<T extends Fruit> {
    void consume(T fruit);
}

public class Consumers {
    Map<Class<? extends Fruit>, Collection<FruitConsumer<? extends Fruit>>> consumers = new HashMap<>();

    public <T extends Fruit> void register(Class<T> clazz, FruitConsumer<T> consumer) {
        consumers.putIfAbsent(clazz, new HashSet<>());
        consumers.get(clazz).add(consumer);
    }

    public <T extends Fruit> void consume(T fruit) {
        Collection<FruitConsumer<? extends Fruit>> cons = consumers.get(fruit.getClass());
        for (FruitConsumer<? extends Fruit> con : cons) {
            // Fair to cast here because we KNOW (because of the key) that it is the right type.
            FruitConsumer<T> c = (FruitConsumer<T>)con;
            c.consume(fruit);
        }
    }
}

public class BananaConsumer implements FruitConsumer<Banana> {
    void register(Consumers c) {
        c.register(Banana.class, this);
        c.register(Apple.class, this); // <-- Now it breaks as expected.
    }

    @Override
    public void consume(Banana banana) {
        System.out.println("Mmm, banana");
    }
}

Agora também observar o comportamento inesperadamente permitiu vai embora.

Acho que você gosta

Origin http://43.154.161.224:23101/article/api/json?id=224392&siteId=1
Recomendado
Clasificación