jjzjj

Java - (匿名子类)在对象实例构造期间覆盖方法

coder 2024-03-16 原文

我正在维护一些 Java 8 代码,如下所示:

Class Entity  {
   protected Model theModel;

   public Entity()  {
       init();
   }

   protected void init()  {
       this.theModel = new Model();
   }
}

Class Model  {
}

Class SubModel extends Model {
}

main {
    Entity newEntity = new Entity()  {
        @Override
        protected void init()  {
            this.theModel = new SubModel();
        }
    };
}

代码目前可以正确编译和运行,但我现在需要更新它。

我的问题是:

  1. newEntity 的构造过程中,init() 方法的覆盖是如何工作的?
  2. 对象构造函数语句中包含的此方法重写的正确术语是什么?

到目前为止,我的研究表明 Java 不能动态覆盖方法 - 不能在此基础上进行覆盖,因为方法覆盖是针对每个类而不是针对每个对象的。但是这个代码片段似乎表明 Java 可以在实践中做到这一点?


更新:请注意,在 main 中创建 newEntity 会创建一个匿名子类,并且正在调用 init() 方法仅针对该匿名子类重写。这在下面的两个优秀答案中得到了更好的解释。

最佳答案

据我所知这里没有什么特别的,只是经典constructor chaining以及应用于虚方法调用的多态性。

当你实例化你的匿名类时,它会自动调用它的 default constructor (由编译器自动给出),在其默认构造函数成功之前,它必须首先调用其父类默认构造函数,该默认构造函数又将调用 init() 方法,因为它已被重写通过您的匿名类,以多态方式结束调用子类中的 init 方法,该方法将模型初始化为您的 SubModel 实例。

约书亚·布洛赫 (Joshua Bloch) 在他的名著 Effective Java 中针对这种模式提出了一些有趣的论点。 ,在 “第 17 项:继承或禁止的设计和文档” 部分,他写道:

“There are a few more restrictions that a class must obey to allow inheritance. Constructors must not invoke overridable methods, directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will get invoked before the subclass constructor has run. If the overriding method depends on any initialization performed by the subclass constructor, the method will not behave as expected. To make this concrete, here's a class that violates this rule:”

然后他继续举了一个你会好好学习的例子:

“Here's a subclass that overrides the overrideMe, method which is erroneously invoked by Super's sole constructor:”

public class Super {
    // Broken - constructor invokes an overridable method
    public Super() {
        overrideMe();
    }

    public void overrideMe() {
    }
}

public final class Sub extends Super {
    private final Date date; // Blank final, set by constructor

    Sub() {
        date = new Date();
    }

    // Overriding method invoked by superclass constructor
    @Override public void overrideMe() {
        System.out.println(date);
    }

    public static void main(String[] args) {
        Sub sub = new Sub();
        sub.overrideMe();
    }
}

“You might expect this program to print out the date twice, but it prints out null the first time, because the overrideMe method is invoked by the Super constructor before the Sub constructor has a chance to initialize the date field. Note that this program observes a final field in two different states! Note also that if overrideMe had invoked any method on date, the invocation would have thrown a NullPointerException when the Super constructor invoked overrideMe. The only reason this program doesn't throw a NullPointerException as it stands is that the println method has special provisions for dealing with a null argument.”

因此,正如您所看到的,正如 Joshua Bloch 解释得很好的那样,风险潜伏在阴影中:在您可以在重写方法中执行的操作的可能性中,您有权访问构造函数链的实例变量还没有机会初始化。关键是在构造函数链完全初始化之前,您不应该被允许接触对象状态。

您可能会说在您的特定情况下不会发生这种情况,因为您没有非法更改状态并且您的覆盖方法是 protected ,而不是公开的,但问题是任何接触此代码的人都需要非常清楚地了解所有这些事情发生在幕后,发生在当前代码以外的地方。在维护期间很容易犯严重的错误,特别是当您或其他开发人员回到这里进行更改时,可能在最初定义后数月甚至数年,并且失去了所有这些危险的背景,有人引入了一个错误将很难找到和修复。

关于Java - (匿名子类)在对象实例构造期间覆盖方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43877035/

有关Java - (匿名子类)在对象实例构造期间覆盖方法的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  5. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  6. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  7. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  8. Ruby 方法() 方法 - 2

    我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

  9. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  10. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

随机推荐