понедельник, 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));