Java——在子类中初始化超类变量?

8

好的,比如说,我有一个名为“Vehicle”的抽象类。Vehicle类中有一些东西,其中之一是一个名为wheels的静态变量,它没有被初始化。我想做的是让其他子类从Vehicle类扩展,比如"Motorcycle"和"Truck",并在这些子类中初始化wheels。

代码:

public abstract class Vehicle {
    static int wheels; //number of wheels on the vehicle
}

但以下内容无法正常工作:
public class Motorcycle extends Vehicle {
    wheels = 2;
}

有没有一种有效的方法来做到这一点?
编辑: 感谢所有回复过我的人。我明白了创建实例可能比将它们放在不同的类中更好,但是我不完全理解java中的“static”,所以我需要一些帮助。
我要为我的程序为摩托车和卡车类分别设置独立的精灵,并且我希望它们是静态的,这样每次创建摩托车或卡车实例时就不必重新加载图像。尽管如此,它们之间几乎具有相同的属性,这就是为什么它们都将扩展自Vehicle超类。
我唯一能看到的另一种方式就是不在Vehicle类中声明sprite变量,而是在Motorcycle/Truck类中,如下所示:
public abstract class Vehicle {
//Other coding
}

public class Motorcycle extends Vehicle {
static BufferedImage sprite = //initialize image
//Other coding
}

public class Truck extends Vehicle {
static BufferedImage sprite = //initialize image
//Other coding
}

6
我认为您可能对Java语言和面向对象编程的一些基本原则存在误解。将“轮子”定义为静态成员似乎不太合适。 - Mike Q
2
你可以详细说明一下你所说的“不起作用”是什么意思。 - Lee Meador
@MikeQ 故意押韵的双关语? - ShahiM
7个回答

12
如果“wheels”是静态的,则只有一个,它将同时适用于所有车辆。因此三轮车、摩托车、18轮卡车和福特汽车都将具有相同数量的车轮,但这对我来说没有意义。最好将“wheels”作为实例变量放在父类中,但每个子类适当地设置它。但是您可以尝试。
Vehicle.wheels = 2;

注意:我正在补充我的答案,因为你在问题中做了添加。

我喜欢你在每个子类中都有静态变量的想法。但是你应该将它们设置为私有。然后在父类(Vehicle)中放置一个抽象方法,如下:

public abstract BufferedImage getSprite();

然后每个直接的子类都必须有相同的方法,并且它可以返回私有静态变量。

将变量设置为静态的,这样您只需要加载一次即可。将它们设置为私有,这样类外部的代码无法干扰它并引入错误。如果可能,可以将它们设置为“final”,这样在类本身中的代码在事后也无法更改它并引入错误(“final”变量不能更改其值,但其值的内容可以更改。因此,“final”不像可能会更好)。


哇,谢谢!这可能是对我最有帮助的评论,而且正是我所需要的。 - user1935527

6
你试图做的事情从根本上是有缺陷的。你可以Motorcycle只初始化一次wheels:
// Static initializer
static
{
    wheels = 2;
}

...或每次创建实例时:

// Instance initializer
{
    wheels = 2;
}

但是只有一个变量 - 不是每个摩托车卡车等都有一个。如果您为卡车摩托车都执行相同的操作,则初始化最后的那个将“获胜”。不过,您想要如何使用该字段还不清楚 - 但如果您只有一个单独的静态字段,则它只会有一个值,而不是每个子类都有一个值。

2

原始类声明:

public abstract class Vehicle {
    static int wheels; //number of wheels on the vehicle
}

public class Motorcycle extends Vehicle{...}
public class Truck extends Vehicle{...}

由于静态变量是属于其声明类的,所以代码无法正常工作。 静态类变量仅为每个类创建一个变量实例内存存储,而不是每个类对象。 当编译器(jvm)在Vehicle类中看到静态变量时,它会为该变量分配内存,并且该内存位置是静态的(不会更改)。 无论是将Vehicle类扩展还是实例化为对象,每次使用都将指向相同的内存位置以获取静态变量。

为了在子类中使用静态变量,必须在方法内部使用它。 因此,您可以基本上像这样重新编写Motorcycle类:

class Motorcycle extends Vehicle{
    public Motorcycle(){
        wheels = 2;
    }
}

代码会被编译,但结果可能出乎意料。例如,如果你在代码中这样做(假设Truck类像Motorcycle类一样声明,并将车轮数赋值为4,并且有一个getter方法返回车轮数的值)。

Motorcycle cycle = new Motorcycle();
Truck pickup = new Truck();
...
System.out.println("Motorcycle has " + cycle.getWheels() + " wheels.");

将打印:

摩托车有4个轮子。


2
我认为有一种更加优雅的方法来完成这个任务。
我即将提出的方案仍然存在一个限制,即您需要一个实例。我没有看到任何解决办法,因为您希望wheels作为超类的一部分被公开,但是wheels的值取决于子类,并且在Vehicle内部没有子类类型的概念,除非有一个实例。
在我看来,在这种情况下,'wheels'既不是静态属性也不是非静态属性。它是类元数据。而指定类元数据的Java方式是通过注释。
您需要一个用户定义的注释,如下所示:
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface VehicleMetadata{
    int wheels();
}

您可以按照以下方式注释Motorcycle:
您可以按照以下方式注释Motorcycle:
@VehicleMetadata(2)
public class Motorcycle extends Vehicle {}

在超类中,您提供了一个访问器来获取注释属性的值。我建议您使用“延迟评估”方法,这样每次需要该值时就不会使用反射。
请注意使用“this”来获取实例:
private String wheelsValue;

public String getWheels() {
    if (this.wheelsValue== null) {

        VehicleMetadatane = null;
        for (Annotation annotation : this.getClass().getAnnotations()) {
            if (annotation instanceof VehicleMetadata) {
                ne = (VehicleMetadata) annotation;
                break;
            }
        }

        wheelsValue = ne.wheels();
    }
    return wheelsValue ;
}

在我看来,这是最优雅的解决方案。

2

静态成员只定义一次,对每个扩展类都是共用的。更改其中一个的值将影响所有其他成员。 这是我认为你真正想要实现的:

public abstract class Vehicle {
    private int _wheels; //number of wheels on the vehicle
    public int getWheels(){return _wheels;}

    protected Vehicle(int wheels){
        _wheels = wheels;
    }
}

public class Motorcycle extends Vehicle {
    public Motorcycle(){
        super(2);
    }
}

public class Car extends Vehicle {
    public Car(){
        super(4);
    }
}

1
如果您在对象中创建一个静态变量,那么对于您创建的每个Vehicle类,即使您为抽象Vehicle类创建另一个子类,该变量也将是相同的。这是由于任何静态变量的“本质”造成的。
我认为您想使用非静态变量,以便可以确定抽象Vehicle类的任何子类的每个实例的轮子值,并且可以按以下方式完成:
public abstract class Vehicle {
    public int wheels; //number of wheels on the vehicle
}

以及任何子类:

public foo extends Vehicle{

     public void someMethode(){
         this.wheels = 2;
     }
}

你也可以对静态变量执行此操作,但这样会更改任何Vehicle子类的每个实例的变量。

希望我能帮到你。


0
也许您想考虑一下您正在使用的构造函数。
public Vehicle(int wheels) {
    this.wheels = wheels; 
}

public Motorcycle(int wheels) {
    super(wheels);
}

public Motorcycle cycle = new Motorcycle(2);

摩托车使用超级构造函数,该函数知道如何处理参数。它会自动将轮子设置为2。


所有的摩托车可能都有同样数量的车轮。这表明将其放在构造函数的参数中不是正确的位置。 - Lee Meador

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接