2009-11-06 2 views
0

Я пытаюсь сделать что-то, что я обычно не делал, это немного странно, но я хотел бы заставить его работать. По сути, у меня есть фабрика, которая должна создавать объекты, вызывая конструктор с различными типами данных (A и B принимают разные типы в коде ниже). Кажется, я получил свое самоустановленное движение по маршруту дженериков (мне нужно, чтобы код был как можно более сложным для времени компиляции). Я не возражаю против написания кода по-другому (я хотел бы оставить идею фабрики, если это возможно, и я не хочу добавлять в приведениях, поэтому параметр «данные» не может быть «объектом»).generics call constructor

Любые мысли о том, как исправить код с помощью дженериков или альтернативный способ его выполнения, который соответствует моим требованиям?

(Технически это домашнее задание, но я инструктор пытается что-то новое ... так что это на самом деле не домашнее задание :-)

public class Main2 
{ 
    public static void main(String[] args) 
    { 
     X<?> x; 

     x = XFactory.makeX(0, "Hello"); 
     x.foo(); 

     x = XFactory.makeX(1, Integer.valueOf(42)); 
     x.foo(); 
    } 

} 

class XFactory 
{ 
    public static <T> X<T> makeX(final int i, 
           final T data) 
    { 
     final X<T> x; 

     if(i == 0) 
     { 
      // compiler error: cannot find symbol constructor A(T) 
      x = new A(data); 
     } 
     else 
     { 
      // compiler error: cannot find symbol constructor B(T) 
      x = new B(data); 
     } 

     return (x); 
    } 
} 

interface X<T> 
{ 
    void foo(); 
} 

class A 
    implements X<String> 
{ 
    A(final String s) 
    { 
    } 

    public void foo() 
    { 
     System.out.println("A.foo"); 
    } 
} 

class B 
    implements X<Integer> 
{ 
    B(final Integer i) 
    { 
    } 

    public void foo() 
    { 
     System.out.println("B.foo"); 
    } 
} 

ответ

1

Я не вижу способа заставить его работать. Я действительно не думаю, что это должно сработать. При вызове функции makeX() вызывающий код должен знать, какой целочисленный параметр соответствует тому, к какому типу данных передается. IOW, ваша абстракция в первую очередь является очень непроницаемой, и то, что вы на самом деле реализуете, является рудиментарной формой полиморфизма, которые вы можете также использовать метод перегрузки для, например:

X makeX(String data) { 
    return new A(data); 
} 

X makeX(Integer data) { 
    return new B(data); 
} 

конечно, это проблема игрушки, и все это. Один из способов заставить его работать - это информировать клиента о классах реализации и добавить аргумент Class<T>, который вы создаете через отражение. Но я полагаю, что это будет победить цель.

+0

Я на самом деле не игрушка, но да, это не суперкритично. Я склоняюсь к этому способу делать это или изменяю тип аргумента, чтобы просто быть строкой. На самом деле строка исходит из argv и представляет либо имя файла, либо строку соединения JDBC. – TofuBeer

+0

Хорошо, я просто собирался «Как бы это реализовать», и это в основном то, что вышло. – wds

1

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

С литьем, у вас есть два варианта

if(i == 0) 
     { 
      x = new A((Integer)data); 
     } 
     else 
     { 
      x = new B((String)data); 
    } 
} 

или

class A 
    implements X<String> 
{ 
    A(final Object s) 
    { 
    } 
} 

... 

class B 
    implements X<Integer> 
{ 
    B(final Object i) 
    { 
    } 
} 
+0

Кастинг (первый способ) не будет работать - просто переместил ошибку компилятора в x = часть.Второй способ - это то, что я не буду делать (это то, что я пытаюсь избавиться). – TofuBeer

+0

Также кастинг (первый способ) не будет работать, потому что вы можете передать Integer вместо String, и он будет компилироваться, но сбой во время выполнения ... Я думаю, мне нужно переосмыслить все это. – TofuBeer

1

Вероятно, ближе всего вы могли бы получить, сохраняя при этом статической безопасности типов и с ленивым строительством является:

public static void main(String[] args) { 
    X<?> x; 

    x = aFactory("Hello").makeX(); 
    x.foo(); 

    x = bFactory(42).makeX(); 
    x.foo(); 
} 

private static XFactory aFactory(final String value) { 
    return new XFactory() { public X<?> makeX() { 
     return new A(value); 
    }}; 
} 

public static XFactory bFactory(final Integer value) { 
    return new XFactory() { public X<?> makeX() { 
     return new B(value); 
    }}; 
} 

interface XFactory() { 
    X<?> makeX(); 
} 

Итак, мы создаем экземпляр абстрактной фабрики, которая создает соответствующий экземпляр wit h соответствующий аргумент. Как завод, продукт изготавливается только по требованию.

Очевидно, что-то должно было дать. Что вы ожидаете от XFactory.makeX(1, "Hello")?

+0

По-прежнему не собираюсь работать над тем, чего я хочу, к сожалению, необходимость вызова двух методов не будет работать, поскольку он побеждает, ставя код позади фабричного метода, но я немного поиграю с ним и посмотрю. По сути, я использую метод, который может создавать разные вещи с другим параметром для конструктора без кастинга ... так что я хотел бы, чтобы «XFactory.makeX (1,« Hello ») выполнял неудачу во время компиляции. .. Я просто не мог придумать, как это сделать. Я придумал несколько способов, но они были довольно уродливыми. Думаю, я просто пытаюсь слишком сильно подталкивать дженерики. – TofuBeer

0

Это невозможно без литья. Как я уже сказал в другом месте - дженерики не устраняют необходимость кастинга, но они означают, что вы можете делать все кастинга в одном месте.

В настройках, которые вы описываете, фабричный метод - это именно то, где происходит вся работа под капотом. Это место, где ваш код говорит компилятору: «Я знаю вы не знаете, что эти типы, но я сделать, так что расслабьтесь.

Это совершенно законно для вашего фабричного метода, чтобы знать, что если г = = 1, тогда данные должны быть типа Integer, а также проверять/применять его при кастинге.

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