我正在关注此事:
http://rickyclarkson.blogspot.com/2006/07/duck-typing-in-java-and-no-reflection.html
我正在尝试改编:
<T extends CanQuack & CanWalk> void doDucklikeThings(T t)
{
t.quack();
t.walk();
}
对此:
public class Activate<D extends CanQuack & CanWalk> {
D d = new MyWaterFowl(); //Type mismatch
}
即使MyWaterFowl实现了这些接口。
我想要一个从未在&lt;&gt;中提及MyWaterFowl的解决方案,因为我最终会注入它(或者其他任何实现这些接口的东西)。
如果你的答案基本上是“你不能那样做,它甚至会是什么类型的?”。请解释为什么它正在为doDucklikeThings方法工作,并确定如果不可能对类做同样的事情,或者如果可能的话,如何去做。
doDucklikeThings中的T必须是有效的,因为它正在工作。如果我把它传递给一个班级,我会传递什么?
这里要求的是MyWaterFowl代码:
package singleResponsibilityPrinciple;
interface CanWalk { public void walk(); }
interface CanQuack { public void quack(); }
interface CanSwim { public void swim(); }
public class MyWaterFowl implements CanWalk, CanQuack, CanSwim {
public void walk() { System.out.println("I'm walkin` here!"); }
public void quack() { System.out.println("Quack!"); }
public void swim() { System.out.println("Stroke! Stroke! Stroke!"); }
}
记得我已经确认doDucklikeThings有效。我需要的语法可以让我注入任何实现所需接口的东西。
答案 0 :(得分:26)
这不起作用,因为类/方法是通用的,您的类/方法的调用者可以将D
设置为MyAmericanEagle
。
Activate<MyAmericanEagle> active = new Activate<>();
然后您的代码将导致
MyAmericanEagle d = new MyWaterFowl();
由于这没有意义(会导致ClassCastException),编译器会拒绝它。
答案 1 :(得分:11)
// Type mismatch
即使MyWaterFowl实现了这些接口。
它不是关于实现这些接口的类型D
(和/或扩展类)。泛型类型变量绑定到特定类型参数。该类型可能与MyWaterFowl
完全不同,因此您无法互换使用它们。
要回答您的编辑,您在两个片段中做了两件完全不同的事情。使用某些边界声明类型变量。因此,它保证是一种实现某种界面(或扩展某些类)的类型,但在任何一种情况下你都不知道它是哪种类型。
我想澄清你做的两件事,即。您对问题的期望以及您在答案中提供的解决方案。
泛型是编译时功能,其中服务器代码,例如
class Activate<D extends CanWalk & CanQuack> {
D instance;
public Activate(D d) {
this.instance = d;
}
public D getInstance() {
return instance ;
}
}
声明类型变量。这是一个变量。在其声明环境中,您在编译时不知道其确切类型。
客户端代码,例如
new Activate<>(new MyWaterFowl());
将类型MyWaterFowl
绑定到Activate
中声明的类型变量。所以客户端代码知道编译时D
是什么。
如果以下
public D getInstance() {
D someRef = new MyWaterFowl();
return someRef;
}
服务器代码中允许,这将失败
Activate<SomeOtherBird> activate = new Activate<>(new SomeOtherBird());
SomeOtherBird reference = activate.getInstance();
泛型保证getInstance()
是类型安全的,因为它被声明为返回绑定到类型变量D
的任何类型。在这种情况下,即SomeOtherBird
。如果允许上面的getInstance()
代码,则类型安全性将被破坏,因为getInstance()
将返回除绑定之外的其他内容。
这并没有改变你的服务器代码(泛型类)中的事实,你知道D
的界限,即。它既是CanQuack
又是CanWalk
。因此,这些类型的对象可以执行任何操作,D
变量引用的对象也可以这样做。
答案 2 :(得分:6)
实际上可以做到。
&lt;&gt;&#;&gt;之间的通用代码很好。方法和类没有什么不同。只需要完成依赖注入:
public class Activate<D extends CanQuack & CanWalk> {
private D d;
Activate(D d) {
this.d = d;
}
void doDuckLikeThings() {
d.quack();
d.walk();
//d.swim(); //Doesn't work
//And shouldn't since that was the whole point of ISP.
}
}
public class MainClass {
public static void main(String[] args) {
Activate<MyWaterFowl> a = new Activate<>(new MyWaterFowl());
a.doDuckLikeThings();
}
}
我以为我会提供一个答案,说明如何解决这个问题。
答案 3 :(得分:2)
扩展Object的类的类型与Object的类型不同。这意味着你无法为其扩展器实例化超类。
编译时:
1 public class Sample<T extends Object> {
2 public Sample() {
3 T t = new Object();
4 }
5 }
您收到incompatible types
错误。
Sample.java:3: error: incompatible types
T t = new Object();
^
required: T
found: Object
where T is a type-variable:
T extends Object declared in class Sample
1 error
与非通用形式时相同:
1 public class Sample{
2 public Sample() {
3 }
4 }
5
6 class SampleExtends extends Sample {
7 public SampleExtends() {
8
9 }
10 }
11
12 class User {
13 public User() {
14 SampleExtends se = new Sample();
15 }
16 }
你从编译器得到这个错误:不兼容的类型
Sample.java:14: error: incompatible types
SampleExtends se = new Sample();
^
required: SampleExtends
found: Sample
1 error
答案 4 :(得分:1)
您希望第二个代码段中D
的实际类型是什么?
让我们说出一些事情:
Activate<Daffy> myActivate = Activate<Daffy>();
那会发生什么?这意味着D
必须属于Daffy
类型,但您尝试将d
设置为MyWaterFowl
的实例。
答案 5 :(得分:1)
你似乎误解了泛型。类型参数<T extends CanQuack & CanWalk>
不对方法或类的范围有效的新类型别名的定义,它是某种类型的占位符,后来由其他人填写。
对于像你的例子那样的通用方法
<T extends CanQuack & CanWalk> void doDucklikeThings(T t)
{
t.quack();
t.walk();
}
方法的调用者决定用哪种类型替换T(在边界规定的限制范围内)。 所以可以使用
doDuckLikeThings(myWaterfowl);
并且编译器猜测你想要的类型参数(可能是myWaterfowl
变量的类型)。
对于泛型类,它是创建类的实例的代码,该类确定实际的类型参数。 所以我可以说
Activate<Duck> duckActivator = new Activate<Duck>();
在此之后,duckActivator
实例是Activate<Duck>
的实例,在其中,D
现在绑定到Duck
。
现在显然你的变量声明说
Duck d = new MyWaterfowl();
这并不起作用。
它也不会在通用方法中工作 - 这个也是无效的:
<T extends CanQuack & CanWalk> void doDucklikeThings()
{
T t = new MyWaterfowl();
}
我想要一个永远不会在
MyWaterFowl
中提到<>
的解决方案,因为我最终会去 只是注入它(或任何其他实现这些接口的东西)。
然后也不要提及它的构造函数。
无论是谁注射你的鸭子对象也必须决定实际的类型参数 - 即这应该是创建Activate
实例的人。在构建Activate
对象后,这不会发生变化。
如果这个限制不适合你(例如,因为同一个Activate
实例需要能够一个接一个地处理不同类型的鸭子对象),也许类型参数是不适合你。
也许带有变量的通配符类型参数的通用包装器可以帮助改为。
采用这种通用包装(或类似的东西):
public class Holder<T> {
private final T t;
public T get() { return t; }
public Holder(T t) { this.t = t; }
}
然后你的Activate类不需要type参数,只是拥有一个带有通配符类型参数的holder变量。 (请注意,实际的Holder实例化具有非通配符类型参数。)
public class Activate {
private Holder<? extends CanQuack & CanWalk> holder;
public <D extends CanQuack & CanWalk> void setDuckLike(D d) {
this.holder = new Holder<D>(d);
}
public Activate() {}
public void doDucklikeThings() {
holder.get().quack();
holder.get().walk();
}
}
现在你做这样的事情:
Activate a = new Activate();
a.setDuckLike(new Duck());
a.doDucklikeThings();
a.setDuckLike(new MyWaterfowl());
a.doDucklikeThings();