понедельник, 6 декабря 2010 г.

Java Generics vs Наследование

Вообщем по просьбе Viktor Davion объясняю (в 140 твиттеровских символов не уложился), отчасти, что побудило к посту ненависти. Под катом, т.к. содержит плоды работы воспалённого мозга.

Вот набросок того кода, который побудил этот пост (упрощённый, по памяти):

  1. package me.drobushevich.blog.generics;  
  2.   
  3. public abstract class AbstractWorker {  
  4.   
  5.     public abstract void execute();  
  6.   
  7. }  
  8.   
  9. package me.drobushevich.blog.generics;  
  10.   
  11. public class Config {  
  12.   
  13.     public final String name;  
  14.   
  15.     public Config(final String name) {  
  16.         this.name = name;  
  17.     }  
  18.   
  19. }  
  20.   
  21. package me.drobushevich.blog.generics;  
  22.   
  23. public abstract class AbstractWorkerFactory<C extends Config> {  
  24.   
  25.     public abstract AbstractWorker createWorker(final C configuration);  
  26.   
  27. }  
  28.   
  29. package me.drobushevich.blog.generics;  
  30.   
  31. import java.util.ArrayList;  
  32. import java.util.List;  
  33.   
  34. public class Service {  
  35.   
  36.     private final List<AbstractWorker> workers;  
  37.   
  38.     public Service(final AbstractWorkerFactory<? extends Config> factory, List<Config> configurations) {  
  39.         workers = new ArrayList<AbstractWorker>();  
  40.         for (Config configuration : configurations) {  
  41.             // не компилится The method createWorker(capture#1-of ? extends Config) in the type AbstractWorkerFactory<capture#1-of ? extends Config> is not applicable for the arguments (Config)  
  42.             workers.add(factory.createWorker(configuration));  
  43.         }  
  44.     }  
  45.   
  46. }  

Всё очень просто, фабрика, которая по Generic конфигурации строит объекты. Сервису должно быть пофигу безразлично, что за конфигурация и т.п. Сервис выглядит странно, т.к. можно было просто передать набор работников, но так надо, тащить все зависимости в блог лениво. Вроде ж выглядит логично, create метод принимает потомка конфигурации, мы потомка конфигурации и даём...

Маленькие пометки.

Зачем вообще дженерики О.о Просто для маленьких вкусностей, вот таких:

  1. package me.drobushevich.blog.generics;  
  2.   
  3. public class ReadConfig extends Config {  
  4.   
  5.     public final boolean option;  
  6.   
  7.     public ReadConfig(final String name, final boolean option) {  
  8.         super(name);  
  9.         this.option = option;  
  10.     }  
  11.   
  12. }  
  13.   
  14. package me.drobushevich.blog.generics;  
  15.   
  16. public class ReadWorker extends AbstractWorker {  
  17.   
  18.     public ReadWorker(final boolean option) {  
  19.     }  
  20.   
  21.     @Override  
  22.     public void execute() {  
  23.     }  
  24.   
  25. }  
  26.   
  27. package me.drobushevich.blog.generics;  
  28.   
  29. public class ReadWorkerFactory extends AbstractWorkerFactory<ReadConfig> {  
  30.   
  31.     @Override  
  32.     public AbstractWorker createWorker(final ReadConfig configuration) {  
  33.         // Обратим внимание на эту строчку (:  
  34.         return new ReadWorker(configuration.option);  
  35.     }  
  36.   
  37. }  

Проблему можно в принципе решить например вот так:

  1. public Service(final AbstractWorkerFactory<Config> factory, final List<Config> configurations) {  
  2. // но не работает  
  3. new Service(new ReadWorkerFactory(), configurations);  

Вообщем жду предложений (; Не исключается возможность ошибки в ДНК. В последнее время за мной было замечено предложение не самых здравых идей ):

Главная проблема, что Generics это уровень компиляции, jvm о них ничего не знает. Кстати, можно посмотреть не очень давнее выступление Джеймса Гослинга, в частности он там говорит, что Generics были реализованы как компромисс между усложнением и полезностью. Трудно спорить (: особенно с ним (:

Кстати, там же он хвалит Scala, вот где, мне нравятся generics, так это в там (к самому языку отношусь не так, хотя тот факт, что на нём можно писать так как на java, но с доп. плюшками, это не плохо).

Если будет настроение, на выходных напишу пост о Generics в Scala. Всё таки generics высших порядков это звучит интересно (:

UPDATE (после обсуждения на stackoverflow):

  1. public AbstractWorker caramba(final AbstractWorkerFactory<? super ReadConfig> factory, ReadConfig configuration) {  
  2.     return factory.createWorker(configuration);  
  3. }  
  4.   
  5. new Service().caramba(new WorkerFactory(), new ReadConfig("1"false));  
  6. new Service().caramba(new ReadWorkerFactory(), new ReadConfig("2"true));  

6 комментариев:

  1. Лол. Джава не нужна.

    А ты на стековерфлове вопрос задавал? Там гуры быстро ответят.

    ОтветитьУдалить
  2. Вопрос, задал просто на всякий случай. Это больше возмущение, так как это не возможно.
    Ну дабы, ещё немного посмеяться, вот ещё один пёрл, того как в самом сане используют дженерики:
    Map<Integer, Double> map = new HashMap<Integer, Double>();
    map.put(1, 2.1);
    //map.put(2.1, 1); - ok, it is not working
    map.get("caramba"); // Is it funny? But it is work: get(Object key)

    ОтветитьУдалить
  3. Нифигово они быстро там в коменты на спамили О.о

    ОтветитьУдалить
  4. Вообщем по результатам обсуждения на stackoverflow обновил пост солюшеном, который относительно не плохо подошел.

    ОтветитьУдалить
  5. Вообщем по результатам обсуждения на stackoverflow обновил пост солюшеном, который относительно не плохо подошел.

    ОтветитьУдалить
  6. Хотя в последнее время много тупил, так что на всякий случай спросил

    ОтветитьУдалить