Как я должен использовать для Java generic с несколькими ограничениями?
Возможно ли передать объект в Java на комбинированный общий тип?
У меня есть метод вроде:
public static <T extends Foo & Bar> void doSomething(T object) {
//do stuff
}
Вызов этого метода не проблема, если у меня есть класс, который реализует оба интерфейса (Foo и Bar).
Проблема заключается в том, когда мне нужно вызвать этот метод, объект, который мне нужно передать, получен как java.lang.Object
, и мне нужно отдать его, чтобы сделать компилятор счастливым. Но я не могу понять, как это сделать.
изменить:
Проблема заключается в такой функции:
public void problemFunction (Object o) {
if ( o instanceof Foo && o instanceof Bar) {
doSomething((Problematic cast) o);
}
}
}
Ответы
Ответ 1
К сожалению, нет законных актеров, которые вы можете сделать, чтобы удовлетворить эту ситуацию. Должен существовать один тип, который, как известно, реализует все интерфейсы, которые вам нужны в качестве границ, так что вы можете использовать их. Возможно, это тип, который вы создаете для этой цели, или какой-либо существующий тип.
interface Baz extends Foo, Bar { }
public void caller(Object w) {
doSomething((Baz) w);
}
Если известны другие типы, например Baz
, для соответствия границам, вы можете протестировать эти типы и иметь ветвь в своем вызывающем абоненте, которая вызывает doSomething
с приведением к этим типам. Это не очень.
Вы также можете использовать делегирование, создав свой собственный класс Baz
, который соответствует границам, требуемым doSomething
. Затем заверните объект, который вы передали, в экземпляр вашего класса Baz
и передайте эту оболочку на doSomething
.
private static class FooBarAdapter implements Foo, Bar {
private final Object adaptee;
FooBarAdapter(Object o) {
adaptee = (Foo) (Bar) o;
}
public int flip() { return ((Foo) adaptee).flip(); }
public void flop(int x) { ((Foo) adaptee).flop(x); }
public void blort() { ((Bar) adaptee).blort(); }
}
public void problemFunction (Object o) {
doSomething(new FooBarAdapter(o));
}
Ответ 2
Java 8 вводит возможность кастинга с дополнительными ограничениями. Вы можете использовать Object
как class
с несколькими interfaces
(или просто как несколько interfaces
).
Итак, это:
doSomething((Problematic cast) o);
просто становится следующим:
doSomething((Foo & Bar) o);
Ответ 3
public static <T extends Foo & Bar> void doSomething(T object)
Это означает, что вы будете выполнять более одной операции над объектом.
Я бы сказал, что если желаемые операции, которые вы выполняете на объекте, достаточно различны для разделения между интерфейсами, тогда они достаточно различны, чтобы заслужить их собственные методы.
Вероятно, вы можете перестроить этот код, чтобы вызвать отдельные методы для выполнения желаемой операции. Это может привести к тому, что вся операция станет более понятной с точки зрения клиента.
Вместо:
public void problemFunction (Object o) {
if (o instanceof Foo && o instanceof Bar) {
doSomething((Problematic cast) o);
}
}
Это будет:
public void problemFunction(Object o) {
if (o instanceof Foo && o instanceof Bar) {
fooifySomething((Foo) o);
baratizeSomething((Bar) o);
}
}
Ответ 4
Можно "поднять" тип объединения, ужасным способом кодирования типа объединения в параметр типа метода; для вашего примера вы можете написать
private <Q extends Foo & Bar> Q upcast(final Object in) {
return (Q) in;
}
// ... elsewhere...
if (myObject instanceof Foo && myObject instanceof Bar) {
doSomething(upcast(myObject));
}
Ответ 5
В качестве обходного пути вы можете определить другой интерфейс FooBar, который расширяет Foo и Bar и реализует этот класс. Он даже не должен быть интерфейсом верхнего уровня - вы можете объявить его приватным, если не хотите загромождать или модифицировать остальную часть вашего кода:
private interface FooBar extends Foo, Bar {}
public void problemFunction (Object o) {
if ( o instanceof Foo && o instanceof Bar) {
doSomething((FooBar) o);
}
}