[英]How to call same tick-method on multiple different objects?
Trying to make JavaFX game but can't wrap my head around how to use tick method on different objects while being able to call other methods outside of interface methods. 尝试制作JavaFX游戏,但无法围绕如何在不同对象上使用tick方法,而又能够调用接口方法之外的其他方法来解决问题。 Made this simplified code for your pleasure:
编写以下简化的代码以使您高兴:
interface TickInterface {
public void tick(); // i.e to move the object or to check for collision.
}
class Car implements TickInterface {
void tick(){
// run on every tick
}
void refuel(){
/*
could be also any other method which is not run
in every tick, like unlocking the car or getLocation()
*/
}
}
class Bicycle implements TickInterface {
void tick(){
// run on every tick
}
}
class LoopClass(){
...
tickInterface car = new Car();
tickInterface bicycle = new Bicycle();
LoopClass(){
ArrayList<TickInterface> rides = new ArrayList<TickInterface>();
rides.add(car);
rides.add(bicycle);
void thisLoopsEveryFrame(){
for(TickInterface ride : rides){
ride.tick();
}
}
void refuelCar(){
car.refuel(); //not possible because of the interface object type
}
}
I want to call tick() on both different objects with same interface but this causes me not being able to call refuelCar() from Car object. 我想在具有相同接口的两个不同对象上调用tick(),但这导致我无法从Car对象调用refuelCar()。 And surely you shoudn't be able to refuel a bicycle.
当然,您应该不能为自行车加油。 What is the standard way of doing the update loop (tick) functionality?
执行更新循环(刻度)功能的标准方法是什么? Frustrating that I couldn't find solutions.
令人沮丧的是我找不到解决方案。
You do the logic inside the class that needs the logic. 您在需要逻辑的类中执行逻辑。
class Car implements TickInterface {
void tick(){
if (lowOnFuel) {
refuel();
{
}
void refuel(){
}
}
-edit- -编辑-
I obviously do not know what you are making exactly but introducing a player changes things. 我显然不知道您在做什么,但是介绍一名球员会改变事情。
I would update/tick your player class and let him know what he is driving since that makes sense. 我会更新/勾选您的玩家级别,并让他知道他在驾驶什么,因为这很有意义。 So if he is driving a
Car
instantiate it by Car playerCar = new Car()
or if you really want to program to an interface (which is good practice in most cases) you can do. 因此,如果他驾驶的
Car
是通过Car playerCar = new Car()
实例化的,或者如果您确实想对接口进行编程(在大多数情况下是一种很好的做法),则可以这样做。
interface Vehicle {
void accelerate();
void steerLeft();
//...
}
If (forwardIsPressed) {
vehicle.accelerate();
}
if (leftIsPressed) {
myCar.steerLeft();
}
if (playerWantsToRefuel) {
if (vehicle instanceof Car) {
// safe to cast into a car object.
Car myCar = (Car) vehicle;
myCar.refuel;
} else if (vehicle instanceof Bike)
{
UI.ShowDialogueBox("You cannot refuel a bike, go eat a something to refuel your energy.");
}
}
As you can see I got rid of TickInterface since that does not make sense anymore. 如您所见,我摆脱了TickInterface,因为那不再有意义。 The player and the AI are driving the cars so perhaps make these have interface 'Driver' with a tick or update function.
玩家和AI正在驾驶汽车,因此也许使它们具有带有滴答或更新功能的“ Driver”界面。 Then let them control the vehicle they drive.
然后让他们控制自己驾驶的车辆。 In a players case if a certain key is pressed you call that function of the car he is driving in the update/tick method that is being called from the game loop.
在玩家的情况下,如果按下某个键,则可以使用从游戏循环中调用的update / tick方法调用他正在驾驶的汽车的功能。 I hope that makes sense.
我希望这是有道理的。
You could still have a Vehicle interface with something like Drive()
where you lower the fuel of the car. 您仍然可以使用带有
Drive()
类的Vehicle接口,在其中降低汽车燃料。 The fuel problem with your bike still remains, again the player needs to know what he is riding in order to make use of it's functionality. 自行车的燃油问题仍然存在,玩家必须再次了解自己在骑什么,才能利用其功能。 Take Grand Theft Auto, all vehicles could have the same interface, just the behavior changes.
以侠盗猎车手为例,所有车辆都可以具有相同的界面,只是行为发生了变化。 But if a GTA car needed to fuel up and a bike would not then a bike would be significantly different then a car.
但是,如果GTA汽车需要加油,而自行车不需要加油,那么自行车和汽车将大不相同。 Still both could inherit that refuel method from an interface, but the bike would display a message that it cannot be refueled, if that does the job for you then great but if it does not make sense it's more then likely bad design.
仍然两者都可以从界面继承该加油方法,但是自行车会显示一条消息,表明它无法加油,如果这样做对您有用,那就太好了,但如果没有意义,则很可能是糟糕的设计。
I also suggest you to read more about interfaces to understand them better. 我还建议您阅读有关接口的更多信息,以更好地理解它们。 Here is a great answer already .
这已经是一个很好的答案 。
TL;DR: you can do TL; DR:您可以
class LoopClass(){
...
Car car = new Car();
Bicycle bicycle = new Bicycle();
LoopClass(){
ArrayList<TickInterface> rides = new ArrayList<TickInterface>();
rides.add(car);
rides.add(bicycle);
}
void thisLoopsEveryFrame(){
for(TickInterface ride : rides){
ride.tick();
}
}
void refuelCar(){
car.refuel(); // possible now car has compile-time type of Car
}
}
Explanation: 说明:
You're confusing "compile-time type" and "runtime type": the statement 您混淆了“编译时类型”和“运行时类型”:
I want to call
tick()
on both different objects with same interface but this causes me not being able to callrefuelCar()
fromCar
object.我想在具有相同接口的两个不同对象上调用
tick()
,但这导致我无法从Car
对象调用refuelCar()
。
is not true. 是不正确的。
The methods that an object actually has , ie the members of the object, are determined by the actual type of the object in memory at runtime (the "runtime type"). 对象实际具有的方法(即对象的成员)由运行时内存中对象的实际类型(“运行时类型”)确定。 This in turn is determined by the constructor that was used to create the object.
这又由用于创建对象的构造函数确定。
So when you write 所以当你写
TickInterface car = new Car();
then when this code is executed at runtime, it creates an object in memory (on the heap) of type Car
. 然后在运行时执行此代码时,它将在内存中(在堆上)创建
Car
类型的对象。 You can think of this object as having both a tick()
method and a refuel()
method. 您可以认为该对象同时具有
tick()
方法和refuel()
方法。
On the other hand, the methods the compiler will allow you to call are determined by the compile-time type : that is, the type of the reference variable used to refer to an object. 另一方面,编译器将允许您调用的方法由编译时类型决定 :即用于引用对象的引用变量的类型。
By writing 通过写
TickInterface car ;
you create a reference (called car
) of compile-time type TickInterface
. 您创建一个编译时类型
TickInterface
的引用(称为car
)。 This means that the compiler will only let you call 这意味着编译器只会让您调用
car.tick();
(because the compiler knows car
is of type TickInterface
, and it knows TickInterface
declares a method called tick()
), but it will not let you do (因为编译器知道
car
的类型为TickInterface
,并且它知道TickInterface
声明了一个称为tick()
的方法),但它不会让您这样做
car.refuel();
because not every TickInterface
instance has a method called refuel()
. 因为并非每个
TickInterface
实例都有一个称为refuel()
的方法。
When you assign a value to car
with 当您为
car
分配值时
car = new Car();
you are performing an upcast . 您正在执行一个下调 。 The type of the expression on the right hand side of the
=
is Car
, while the type of the expression on the left hand side is TickInterface
. =
右边的表达式的类型是Car
,而左边的表达式的类型是TickInterface
。 Since the compiler is assured that every Car
instance is also a TickInterface
instance, this is perfectly legal. 由于可以确保编译器确保每个
Car
实例也是TickInterface
实例,因此这是完全合法的。
When you add car
to your list: 将
car
添加到列表中时:
rides.add(car);
you effectively create a second reference to the Car
object you created. 您可以有效地创建对创建的
Car
对象的第二个引用。 The second reference is kept internally in the List
. 第二个引用保留在
List
内部。 Since you declared the list to be of type TickInterface
, with 由于您将列表声明为
TickInterface
类型,因此
List<TickInterface> rides = new ArrayList<TickInterface>();
you can think of that hidden internal reference as being of compile-time type TickInterface
as well. 您也可以认为该隐藏的内部引用也属于编译时类型
TickInterface
。
However, there is no reason for both these references to be the same type. 但是,这两个引用没有理由是同一类型。 You can do
你可以做
Car car = new Car();
Bicycle bicycle = new Bicycle();
LoopClass(){
ArrayList<TickInterface> rides = new ArrayList<TickInterface>();
rides.add(car);
rides.add(bicycle);
void thisLoopsEveryFrame(){
for(TickInterface ride : rides){
ride.tick();
}
}
Now car
has compile-time type Car
(and bicycle
has compile-time type Bicycle
). 现在,
car
具有编译时类型Car
(而bicycle
具有编译时类型Bicycle
)。 The call 通话
rides.add(car);
is perfectly legal: rides.add(...)
is expecting something of type TickInterface
, and you are giving it a Car
: the compiler again is assured that every Car
instance is also an instance of TickInterface
. 是完全合法的:
rides.add(...)
期望的是TickInterface
类型的TickInterface
,并且您给它提供了Car
:再次确保编译器确保每个Car
实例也是TickInterface
的实例。 In this version, you have moved the upcast to this point in the code, instead of to the assignment to car
. 在此版本中,您已将upcast移至代码中的这一点,而不是分配给
car
。
Now, because the compile-time type of car
is Car
, the method you wanted to write: 现在,由于
car
的编译时类型为Car
,所以您要编写的方法是:
void refuelCar(){
car.refuel();
}
will compile and execute just fine. 将编译并执行就好了。
It is bad approach, but you can : 这是不好的方法,但是您可以:
((Car) car).refuel();
I think you have to create interface with name like Refuel
and void refuel();
我认为您必须使用
Refuel
和void refuel();
类的名称创建接口void refuel();
-method. -方法。
And you have to decide: 您必须决定:
inheritance -> interface Refuel extends TickInterface
and class Car implements Refuel
继承 ->
interface Refuel extends TickInterface
并且class Car implements Refuel
or implementation -> class Car implements Refuel,TickInterface
. 或实现 ->
class Car implements Refuel,TickInterface
。
It depends of you tasks and architecture of application. 它取决于您的任务和应用程序的体系结构。
Like already mentioned, if you just want to keep your current architecture, you could always explicitly typecast the object to its subclass and easily access all of its specialized public methods in this way. 如前所述,如果您只想保留当前的体系结构,则可以始终将对象显式转换为它的子类,并以这种方式轻松访问其所有专用的公共方法。 You can also use the
instanceof
operator to check the instance type of the object first to ensure that you can apply the method. 您还可以使用
instanceof
运算符首先检查对象的实例类型,以确保可以应用该方法。 However, I also think it is bad practice to use it in your context. 但是,我也认为在您的环境中使用它是不好的做法。 Inheritance / interfaces are usually used to abstract common properties shared by the subclasses, independent from their concrete implementation.
继承/接口通常用于抽象子类共享的公共属性,而与它们的具体实现无关。 So it makes sense to share the "tick"-property, but your
LoopClass
should not have to care about what happens in each tick. 因此,共享“滴答”属性
LoopClass
,但是LoopClass
不必关心每次滴答会发生什么。
I would recommend you to outsource the control logic in separate classes (or include it in your game object instance classes if you want) and use the explicit subtypes of the objects. 我建议您将控制逻辑外包给单独的类(如果需要,也可以将其包括在游戏对象实例类中),并使用对象的显式子类型。 If your control class needs to modify both vehicles, it makes sense that it has knowledge of both ones.
如果您的控制班需要同时修改这两辆车,那么就可以理解这两辆车。 So eg your
Car
class could use a reference to a Bicycle
object (maybe provided by an own attribute) and manipulate both objects in its tick()
method if a collision occurs. 因此,例如,您的
Car
类可以使用对Bicycle
对象的引用(可能由自己的属性提供),并在发生碰撞时在其tick()
方法中操纵这两个对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.