2009-03-25 2 views
29

У меня довольно сложный набор общих классов в Java. Например, у меня есть интерфейсТипичные псевдонимы для генераторов Java

interface Doable<X,Y> { 
    X doIt(Y y); 
} 

и реализацию

class DoableImpl implements Doable<Foo<Bar<Baz,Qux>>,Foo<Bar<Zot,Qux>>> { 
    Foo<Bar<Baz,Qux>> doIt(Foo<Bar<Zot,Qux>> fooBZQ) { ... } 
} 

В реальной реализации Doable имеет довольно много методов и так Foo<Bar<Baz,Qux>> и т.д., появляются снова и снова. (Верьте или нет, общие типы немного более болезненны, чем это. Я упростил их для примера.)

Я хотел бы упростить их, чтобы сохранить себя печатать и облегчить деформацию на моих глазах. Я хотел бы иметь простой «псевдоним типа» для Foo<Bar<Baz,Qux>> и т. Д., Например FooBBQ и FooBZQ.

Моя текущая идея состоит в том, чтобы определить классы-оболочки:

class FooBBQ { 
    public static FooBBQ valueOf(Foo<Bar<Baz,Qux>> fooBBQ) { 
    return new FooBBQ(fooBBQ); 
    } 
    private Foo<Bar<Baz,Qux>> fooBBQ; 
    private FooBBQ(Foo<Bar<Baz,Qux>> fooBBQ) { 
    this.fooBBQ = fooBBQ; 
    } 
    public Foo<Bar<Baz,Qux>> toGeneric() { 
    return fooBBQ; 
    } 
} 

class FooBZQ { /* pretty much the same... */ } 

class DoableImpl implements Doable<FooBBQ,FooBZQ> { 
    FooBBQ doIt(FooBZQ fooBZQ) { ... } 
} 

Это хорошо работает, но у него есть несколько недостатков:

  1. Нам необходимо определить отдельные обертки для каждого родового экземпляра. Классы-оболочки являются короткими и стилизованными, но я не могу определить способ их макроопределения.
  2. У нас есть служебные данные по переводу (концептуально, если не оперативно) для вызова valueOf и toGeneric для преобразования между FooBBQ и Foo<Bar<Baz,Qux>>. Например, если doIt вызовы в некоторой библиотеке рутиной, ожидающей Foo<Bar<Zot,Qux>> (который делает реальное осуществление), мы в конечном итоге с чем-то вроде

    return FooBBQ.valueOf(libraryCall(fooBZQ.toGeneric())) 
    

    , где мы бы имели первоначально

    return libraryCall(fooBZQ); 
    

Есть ли другой способ получить поведение типа «псевдоним», которое я хочу здесь? Возможно, используя некоторые сторонние инструменты для макросов? Или мне нужно принять, что мне придется много набирать, одним способом (используя общие типы в реализации) или другим (писать обертки для них)? Может быть, если это много общих параметров, которые летают, это всего лишь плохая идея, и мне нужно переосмыслить проблему?

[UPDATE] ОК, я запрещаю дальнейшие ответы «не делай этого». Возьмите его как данность, что Foo<Bar<Baz,Qux>> имеет истинное значение в моей проблемной области (Пит Киркхам может быть прав, что у него есть достаточно значение, чтобы получить правильный класс-оболочку с описательным именем). Но это проблема программирования; не пытайтесь определить проблему.

+0

Я собираюсь сказать «возможно, да» до последнего, но на самом деле нет конструктивной обратной связи: P Похоже на беспорядок! Удачи! –

+6

Я думаю, что этот вопрос будет хорошим предложением для Java. Если вы могли бы предложить функцию «Тип слияния» для Oracle, я думаю, что это будет отличная языковая функция, которая упростит язык и сделает исходный код более удобочитаемым. –

+1

Колено глубоко в Java сегодня и захотелось очистить некоторый подробный код ... не только я не получаю вывод типа, но и не накладываю тип. * sob * –

ответ

2

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

Очень вероятно. Нужно ли специализировать «Doit» в 8 измерениях?

Во многих случаях эти типы не существуют в вакууме, и вы должны думать о том, какие объекты домена представляют вашу «оболочку», а не использовать их в качестве удобства кодирования.

0

Я бы сказал, что да, вам нужно переосмыслить проблему. Заявление

class DoableImpl implements Doable<Foo<Bar<Baz,Qux>>,Foo<Bar<Zot,Qux>>> { 
    Foo<Bar<Baz,Qux>> doIt(Foo<Bar<Zot,Qux>> fooBZQ) { ... } 
} 

- довольно ясный случай использования дженериков.

+0

Я не согласен. Возможно, это не чрезмерное использование дженериков ... это может быть просто нечеткое использование дженериков. Если это обеспечивает дополнительную безопасность типа, то это может стоить боли, но также должен быть способ сделать это более четко. – Eddie

+0

Вы поверили бы мне, если бы я сказал, что это действительно ясно (просто чрезвычайно многословно) в реальном коде? Очевидно, нет никакого смысла в параметризации ваших объектов с помощью Foo, Bar и Qux ... –

+0

Я видел (и делал) хуже, бывают моменты, когда это имеет смысл ... но им может быть трудно следовать. Однако эти типы могут стоить того. – TofuBeer

9

Если вам нужна безопасность в полном объеме, я не думаю, что вы можете сделать это лучше, не используя какие-либо классы-обертки. Но, почему бы не сделать эти классы наследуют/реализовывать оригинальные общие версии, например:

public class FooBBQ extends Foo<Bar<Baz,Qux>> { 
... 
} 

Это устраняет необходимость toGeneric() метода, и это более ясно, на мой взгляд, что это всего лишь псевдоним типа , Кроме того, общий тип может быть отлит в FooBBQ без предупреждения компилятора. Лично я хотел бы сделать интерфейсы Foo, Bar, Baz..., если это возможно, даже если в реализации произойдет некоторое дублирование кода.

Теперь, не зная домена конкретной проблемы, то трудно сказать, нужно ли вам, скажем, FooBBQ, как в вашем примере, или, возможно:

public class FooBar<X, Y> extends Foo<Bar<X, Y>> { 
... 
} 

С другой стороны, вы думали о просто настроить компилятор Java, чтобы не показывать некоторые общие предупреждения, и просто опустить части общего определения? Или, используйте стратегически расположенный @SuppressWarnings("unchecked")? Другими словами, вы можете сделать DoableImpl только «частично обобщенного»:

class DoableImpl implements Doable<Foo<Bar>>,Foo<Bar>> { 
    Foo<Bar> doIt(Foo<Bar> foobar) { ... } 
} 

и игнорировать предупреждения ради меньшего кода беспорядком? Опять же, трудно решить без конкретного примера, но это еще одна вещь, которую вы можете попробовать.

+0

Расширение Foo > дает вам односторонний перевод бесплатно (т. Е. Неявное ускорение) за счет необходимости писать более сложные конструкторы (для явного литья вниз). Мне непонятно, что это победа. Я хотел бы как можно больше избегать аннотаций @SuppressWarnings. –

5

Scala имеет приятную поддержку псевдонимов типов. Например:

type FooBBQ = Foo[Bar[Baz,Qux]] 

Я понимаю, что этот ответ не поможет, если у вас нет возможности переключиться на Scala. Но если у вас есть возможность переключения, вам может быть легче.

1

Ну, у Java нет псевдонимов типа, так что вам не повезло. Однако псевдонимы типов иногда могут быть заменены переменными типа! Таким образом, мы решаем проблему слишком большого количества дженериков с еще большим количеством дженериков!

Как вы раздели все содержимое из вашего примера, я не могу угадать, где и есть ли смысл вводить дополнительные переменные типа, но вот один возможный распад:

class DoableImplFoo<A,B> implements Doable<Foo<A>,Foo<B>> { 
    public DoableImplFoo(SomePropertyOf<A,B> aAndBAreGoodEnough) { ... } 
    Foo<A> doIt(Foo<B> fooB) { ... } 
} 

При создании экземпляра A к Bar<Baz,Qux> и B до Bar<Zot,Qux> позже вы можете обнаружить, что есть еще один шаблон, но он может быть меньше, чем у вас первоначально.

Смежные вопросы