你正在寻找的东西或多或少是可能的,但我们需要在这里解开一些层.
它是CameraGun吗,还是语义上有关系?
现在您有以下情况:两个完全不相关的接口,每个接口声明一个方法,它们的签名完全相同.或者,如果不是一模一样,也可以是近乎一模一样.暂时忘记,由于擦除,它们有so个相同之处(仅在泛型上不同),所以Java不能为您工作.只需考虑这样一个事实,即这些方法签名在两个utterly无关的类型上有nearly个相同.
所以,你所拥有的就相当于这个:
public interface Camera {
void shoot(Person p);
}
public interface Gun {
void shoot(Person p);
}
// and with those established, you are attempting to...
class CameraGun implements Camera, Gun {
void shoot(Person p) {
// hoo boy I wonder what we are going to write here!
}
}
因此,只有两种 Select .
你真的需要一个CameraGun:一个类需要实现一个方法,以某种方式同时做两个utterly件不相关的事情,就像CameraGun需要提供一个方法来实现拍摄一个人的照片和谋杀他们的概念,以及YOU SHOULD OBVIOUSLY BE RUNNING FOR THE HILLS HERE一样.唯一正确的答案是不要玩这个游戏.Do not写这个类.问题是‘CameraGun?’最好的回答是:"什么??没有相机枪!"
这不是一个摄像机的例子:你的两个不相关的apply
个方法实际上并不是不相关的.它们在语义上的意思是一样的.不像那个相机/枪的场景.例如,在相机/枪的场景中,这两种方法都被称为shoot
,这或多或少是巧合.相机可以很容易地命名为void capture(Person p)
,但这个名字对枪接口毫无意义.这是一个提示,相机枪的情况是,嗯,第一个案件.如果在这里不能做到这一点(没有一个适合一个的名字会不适合另一个),那么,整个设计都是错误的.
所以,这条路在这里Forking 了.你要么有一把CameraGun,在这种情况下你就得停下来.正确的.现在.
或者,您有一个‘2个语义等价的概念,由方法声明表示;但是,它们位于无关的类型中’,在这种情况下,我们将继续讨论...
您需要一个通用的超类型
如果这两个apply
方法在语义上相关,则your code needs to reflect that fact.现在它没有,因此,你的代码被 destruct 了.在java中实现这一点的方法是创建一个通用类型来捕获这种共享的语义含义.接口被允许扩展另一个接口,并重新定义你的扩展因此"采用"的方法背后的语义思想-但你不能取消定义,也不能改变任何东西.你只能收紧定义(基本接口可能会说:"节日的事情会发生",而一些扩展它的接口则会说:"五彩纸屑会发生".我们都知道这是一个节日.".子接口并没有改变这个方法是通过做一些节日的东西来实现的.它只是增加了一个兼容的和更具体的附加要求.
这就是你必须在这里建造的.啊,但是,非专利药.嗯,你需要...
超级
你的代码定义甚至在我们到达"它不能编译"部分之前就已经被 destruct 了.这一点:
default Parent1 apply(Consumer<Parent1> consumer) {
consumer.accept(this);
return this;
}
Is just wrong.
这是正确的:
default Parent1 apply(Consumer<? 超级 Parent1> consumer) {
consumer.accept(this);
return this;
}
区别在于? 超级
.这个消费者的重点是消费this
.Consumer<GrandParent>
号和Consumer<Object>
号都能做到.没有理由不写这个.
一旦你修好了,你就需要..
泛型'self 类型'黑客.
不幸的是,Java没有自类型.你需要养成的第一件事就是接受事实.它无法真正修复,只能四处破解.这意味着拥有随时返回方法的层次 struct 是相关的is just wrong,并且是您需要停止使用的代码样式.我有时会觉得这很诱人.但是,在这里,考虑到类型层次 struct clearly扮演了一个角色,让这些方法返回自己是一个非常糟糕的 idea .只需使用void
,调用代码将不得不放弃能够链接的便利.
然而,如果真的需要自己的类型,sometimes,你可以破解它.这并不是没有问题,不能应用向后兼容性,所以,您必须立即考虑它.它看起来是这样的:
interface Grandparent<G extends Grandparent<G>> {
@SuppressWarnings({"all", "unchecked"})
default G self() {
return (G) this;
}
}
interface Parent1 extends Grandparent<Parent1> {}
interface Parent2<P extends Parent2<P>> extends Grandparent<P> {}
class Child1 implements Parent2<Child1> {
public static void main(String[] args) {
Child1 c = new Child1();
Child1 d = c.self();
assert c == d;
}
}
正如您所看到的,此黑客攻击具有与其相关的各种问题:
- 您必须明确地编写
self()
方法,并且不添加一些@SuppressWarnings
就不能编写它.至少,一旦您这样做了,任何其他方法都不再需要进入@SW
区域;它们只需在需要时调用self()
即可.
- 层次 struct 中的任何子类型都必须继续拼凑起来,并添加这奇怪的
<X extends Self<X>>
个东西.你必须预先决定你是不是层级 struct 中的最后一种类型.
- 即使是这样,任何实现都需要编写
class Foobar extends SelfHackeryType<Foobar>
个代码.
- 任何使用这种实例的人都会看到泛型,并可能会问:见鬼,这是怎么回事?
但是,如果你愿意跳过所有这些障碍,你就会得到你想要的:你可以拨打child1.someSelfReturningMethod()
,而这个表达式的类型是‘Child1’.这意味着如果‘Child1’有一个你想要链接的方法setName
(它返回自身),而Groud1(extends Child1
)有setBirthdate
(Child1没有这个方法),你仍然可以写new Grandchild1().setName("Jake").setBirthdate(someDate);
并拥有那个‘work’--而如果没有这个攻击,它就不会工作,因为setName
被声明返回Child1
,而does not have是setBirthdate
方法.
把它放在一起
import java.util.function.*;
interface Grandparent<G extends Grandparent<G>> {
@SuppressWarnings({"all", "unchecked"})
default G self() {
return (G) this;
}
default G apply(Consumer<? 超级 G> consumer) {
consumer.accept(self());
return self();
}
}
interface Parent1<P extends Parent1<P>> extends Grandparent<P> {}
interface Parent2<P extends Parent2<P>> extends Grandparent<P> {}
class Child implements Parent1<Child>, Parent2<Child> {
String name;
}
class Example {
public static void main(String[] args) {
Child c = new Child();
c.name = "Jake";
Consumer<Object> printer = System.out::println;
Consumer<Child> announcer = x -> System.out.println("Welcome to the world, " + x.name + "!!");
Child d = c.apply(printer);
Child e = c.apply(announcer);
assert c == d;
assert c == e;
}
}