2013-07-06 92 views
2

我去'刷新'我的Java,只是意识到,显然我不懂基本概念!这里有一个简单的我想不通:Java抽象类+继承 - 范围或名称解析

public abstract class Robot { 

    private String model = "NONAME"; 

    public Robot() { 
     System.out.println("Making a generic " + model + " robot, type: " + this.getClass()); 
    } 

    public String getModel() { 
     return model; 
    } 
} 

OK,和子类:

public class Terminator extends Robot { 
    private String model; 

    public Terminator(String model) { 
     super(); 
     System.out.println("Making a " + model + " terminator, type: " + this.getClass()); 
     this.model = model; 
    } 
} 

然后我运行一个简单的例子,希望“T1000”要打印:

Robot r1 = new Terminator("T1000"); 
    System.out.println(r1.getModel()); 

没有骰子!打印“NONAME”。在此之前,我从这个构造函数输出:

  • 制作一个通用的NONAME机器人类型:类com.akarpov.tutorial.Terminator
  • 制作T1000终止,类型:类com.akarpov.tutorial.Terminator

因此,我看到Java发现我的类的运行时实例是Terminator,这是被告知要做什么的新事实。显然,终结者实例确实保留了模型==“T1000”的副本。但在调试器(IntelliJ)中检查r1对象我在不同的地址(显然)使用不同的字符串看到名为'model'的变量两个。显然,如输出所示,抽象类中的getModel将拾取Robot类中定义的默认值,而不是传递给Terminator构造函数(并保留在对象内)的默认值。

这是什么我不明白抽象类和继承,以及我将如何去有一个默认值和默认行为(即getModel),其中拿起一个特定的数据(即“T1000”)在一个子类? 谢谢!如果这个问题已经被解决了很多次,我很抱歉 - 我看了看,但没有看到我跳过。

回答

1

你遇到的问题是,你确实创造了两个变量。现在,使用您的代码,当您拨打r1.getModel()时,您将获得原始基类model

如果您希望能够从子类设置模型,您有几个选项。你可以通过声明一个新的String model来改变开始的方向,但是你必须继续重写超类中的getModel()方法,这样你的子类将会看到它自己的model而不是超类model

public class Terminator extends Robot { 
    private String model; 

    public Terminator(String model) { 
     super(); 
     System.out.println("Making a " + model + " terminator, type: " + this.getClass()); 
     this.model = model; 
    } 

    @Override 
    public String getModel(){ 
    return model; 
    } 
} 

另一种选择是在超类中为model创建一个公共受保护的setter方法。

public abstract class Robot{ 

    private String model = "NONAME"; 

    public Robot() { 
     System.out.println("Making a generic " + model + " robot, type: " + this.getClass()); 
    } 

    public String getModel() { 
     return model; 
    } 

    protected void setModel(String str){ 
     this.model = str; 
    } 
} 

然后你只想有你的终结者对象调用setModel(model)getModel()之前,你将有所需的输出。

+0

你说得对,当然 - 这是我在发布2分钟后意识到的。不过,我已经接受了答案,否则我会选择你的! – alexakarpov

2

您的问题是与私人修改器...模型变量在两个类中分别存在两次。私人手段只对那个班级可见。您可能想要使用setter方法。

+0

我在发帖2分钟后发现,当我去和我的配偶谈论这个问题时 - 问题不是私人的(我尝试保护),而是因为我*将机器人模型藏在另一个模型在终结者!从终结者中移除该变量解决了这个问题。 – alexakarpov

+0

......开始思考这个问题,你比我第一个想法更正确 - 因为我确实忽视了这样一个事实,即私生子不是遗传的。 – alexakarpov

1

哦天哪我知道了之后发帖。 我的错误是在Terminator中声明另一个String模型,导致在Robot中隐藏模型 - 因此是两个副本。删除它解决了这个问题。哎呀!

+0

只是删除变量声明不能解决问题。您还需要向基类添加一个字符串构造函数,并从子类中委托给它,并在子类构造函数中删除'this.model = model'。 –

+0

我认为你在一般情况下是正确的 - 但在我的情况下,默认情况下,基类Robot将模型字段初始化为“NONAME”。因此,如果我的理解是正确的,那么从终结者中移除模型字段使得超类模型成为唯一 - 并且这是解决的子类中的一个“模型”。 – alexakarpov

+1

私有成员不会被继承,所以只有当您碰巧在'Robot'内声明'Terminator'为一个静态嵌套类时才会工作。或者,当然,如果您在此期间提高了'model'的访问级别。 –

0

让我们通过下面这行代码

Robot r1 = new Terminator("T1000"); 

所以这就要求终结者(String)构造。构造函数的第一件事是明确地调用超类的构造函数。它会自动完成,但你已经明确地写了super(),没关系。父类的构造做1两件事:

System.out.println("Making a generic " + model + " robot, type: " + this.getClass()); 

好了,所以它打印出“让通用NONAME机器人类型:终结者”,因为这是该方法所看到的。它没有对任何“模型”变量的本地引用,因此它使用Robot类中定义的实例变量。然后控制返还给终结者构造函数,它进行打印出来

System.out.println("Making a " + model + " terminator, type: " + this.getClass()); 

但是这一次它的工作原理,你如何期望,因为模型变量是由类调用的方法传递,因此它阴影的实例变量。因此它的值是“T1000”。希望这是有道理的

+0

是的,你是对的。这解释了为什么在Terminator构造函数中我看到“T1000”;但我的问题是让抽象类具有通用的“getModel”行为以某种方式与子类“共享状态” - 由于我已将“T1000”分配给子类实例变量,因此该子类无法工作,而超类方法只能看到它自己的 - 单独的实例变量。不过,接受你指出本地vs实例的答案,我也错过了! – alexakarpov