我正在尝试编写一个接受功能接口的泛型方法。见例子。任何帮助表示赞赏。
更新:编译并运行。对改进或澄清的意见或建议感兴趣。
我不会创建一个Typed Abstract Base Class。在repeat方法中添加一个额外的参数,指定执行命令的机器人似乎与其他类似的示例更加一致。
思想?
import java.util.function.Consumer;
/**
* The intent is to create a generic function repeat() in the Robot base class
* that will repeat actions for all subclasses of robots
*/
public class GenericFunctionalInterface
{
public static void main(String[] args)
{
// Consumer<Robot> actionTurn = Robot::turnLeft;
// Consumer<EnhancedRobot> anyAction = EnhancedRobot::anyAction;
Robot r = new Robot();
r.repeat(3, a->a.turnLeft() );
EnhancedRobot e = new EnhancedRobot();
e.repeat(3, a->a.anyAction() ); // cannot find symbol - method anyAction()
// if ((EnhancedRobot)a).anyAction() is used this works
}
}
class Robot
{
public void move() { System.out.println("MOVE"); }
public void turnLeft() { System.out.println("TURNLEFT"); }
public <T extends Robot> void repeat(int n, Consumer<T> cmd )
{
for(int i=0; i<n; i++) cmd.accept( (T) this); // UPDATED: casting this to T removed -> incompatible types: Robot cannot be converted to T
// this will be whatever type repeat is called on
}
// added this override - specifying what robot to send the command to
public <T extends Robot> void repeat(int n, Consumer<T> cmd, T robot )
{
for(int i=0; i<n; i++) cmd.accept( robot );
}
}
class EnhancedRobot extends Robot
{
public void turnRight() { System.out.println("TURNRIGHT"); }
public void move() { System.out.println("MOVEOVERIDE"); }
public void anyAction() { System.out.println("ANYACTION"); }
}
答案 0 :(得分:1)
对于e.repeat()
问题:假设您有以下代码:
class SuperRobot extends Robot {}
EnhancedRobot e = new EnhancedRobot();
e.repeat(3, new Consumer<SuperRobot>() {
@Override
public void accept(SuperRobot t) {
// ...
}
});
然后在想要调用的重复方法内:
Consumer<T> cmd;
cmd.accept(this);
这会失败,因为this
属于EnhancedRobot
类型,但cmd
期望得到SuperRobot
。这就是你不能在那里打电话的原因。
存在a.anyAction()
问题,因为a
类型为Robot
,但没有anyAction()
方法。从声明(T extends Robot
),关于其类型的唯一安全假设是Robot
或扩展它的东西。因此,您无法从EnhancedRobot
上调用方法。
答案 1 :(得分:1)
您需要以Robot
的方式将Consumer
的{{1}}类型参数化Enum
,并添加返回适当类型{{1}的方法(这要求this
是抽象的,因为任何默认实现都必须错误地返回Robot
)。
null
稍微更一般地说,我认为你可以将abstract class Robot<THIS extends Robot<THIS>> {
...
public void repeat(int n, Consumer<THIS> cmd) {
for (int i=0; i<n; i++) {
cmd.accept(getThis());
}
}
protected abstract THIS getThis();
}
class EnhancedRobot extends Robot<EnhancedRobot> {
@Override protected abstract EnhancedRobot getThis() {
return this;
}
...
}
声明为
repeat
答案 2 :(得分:0)
在更新的代码中,repeat
方法不使用您正在调用方法的Robot
实例。所以方法可以是static
。
// added this override - specifying what robot to send the command to
public static <T extends Robot> void repeat(int n, Consumer<T> cmd, T robot )
{
for(int i=0; i<n; i++) cmd.accept( robot );
}
像
一样被调用EnhancedRobot e = new EnhancedRobot();
Robot.repeat(3, a->a.anyAction(), e );
但是robot
方法不需要repeat
参数。您可以将其声明为
public static void repeat(int n, Runnable cmd)
{
for(int i=0; i<n; i++) cmd.run();
}
像
一样被调用EnhancedRobot e = new EnhancedRobot();
Robot.repeat(3, () -> e.anyAction());
显示了设计的实际问题。重复操作是否与Robot
实例相关,与repeat
方法的逻辑无关。试图通过通用签名来强制执行这种关系会产生问题而没有任何好处。
事实上,已经存在允许重复的通用工具。例如,你可以写
EnhancedRobot e = new EnhancedRobot();
Collections.nCopies(3, e).forEach(EnhancedRobot::anyAction);
或
EnhancedRobot e = new EnhancedRobot();
IntStream.range(0, 3).forEach(i -> e.anyAction());