【问题标题】:Question on the nature of inherited Java classes关于继承Java类的性质的问题
【发布时间】:2011-03-28 18:56:30
【问题描述】:

所以我想我有一个非常基本的问题。假设您在项目 com.bee.buzz 中包含了一个名为 com.cow.moo 的开源 Java 程序。

moo 有很多很棒的课程,其中大部分是您不想接触的,但有几个是您愿意接触的。现在,最好的办法是扩展您要修改的类,对吧? (我知道有很多关于扩展与实现的说法,但这些类都不是接口,所以这是不可能的。)

我的问题是,说这是 moo 中的课程:

package com.cow.moo;
public class Milk {
    private float currentMilk;
    public int getMilk() { /* Stuff */ }
    public float convertToGallons (float liquid) { /* More Stuff */ }
}

现在,假设我只想在扩展 Milk 的新类中使用 getMilk。但是,Milk 中的 getMilk 依赖于私有变量(如 currentMilk)和其他我不会包含的函数(如 convertToGallons。)如果我希望我的新函数正常工作,我是否必须包含这些其他变量和函数?我不想对功能进行大量修改,只需添加一点即可。最好的方法是什么?

一般来说,构建更大项目的技巧也会很有用。我认为这里的一些 Java 专家甚至不需要五秒钟就能得出答案。感谢您的宝贵时间。

【问题讨论】:

    标签: java oop inheritance composition


    【解决方案1】:

    现在,假设我只想在扩展 Milk 的新类中使用 getMilk。但是,Milk 中的 getMilk 依赖于私有变量(如 currentMilk)和其他我不会包含的函数(如 convertToGallons。)如果我希望我的新函数正常工作,我是否必须包含这些其他变量和函数?

    您不必包含 public 函数和变量。继承的核心概念是,作为子类,您可以免费获得包含在子类中的所有父类的公共(和受保护)成员。所以你的子类(比如说 HoneyMilk)可以从一开始就调用convertToGallons

    在这种情况下覆盖getMilk 会更加棘手,因为它依赖于私有变量(您的子类无法访问该变量)。我的建议是将您的思维方式从将课程视为“白盒子”转变为“黑盒子”。我的意思是你应该实现getMilk 的覆盖版本,就好像你实际上看不到Milk 的源代码一样。虽然它看起来像是一个迂回的解决方案(我的意思是,为什么我不能在这里调整这条线?!),这将迫使您仅使用父类公开公开的内容来实现您的子类。它还强调了抽象的重要性,在开发大型项目时使用它绝对是至关重要的。

    【讨论】:

    • 这就是我一直在寻找的答案,谢谢。面向方面的编程和反射对于我想要在这里完成的事情有点遥不可及。再次感谢。
    【解决方案2】:

    除非您使用 Java 反射,否则您将无法使用私有类成员,这会有点难看。如果我是你(并且更改不是太重,在这种情况下我会分叉原始项目),我会考虑在运行时修改代码或静态使用方面编织(面向方面​​的编程)。 AspectJ 可能看起来好像有一个尖锐的学习曲线,但它是一个很好的工具,可以放在您的工具箱中,并且完全符合您的需求。

    【讨论】:

      【解决方案3】:

      一般建议是优先考虑组合而不是继承

      说,你有一个接口和一个现有的实现,最适合你的需要,比如

      public interface MilkProvider { public float getMilk(); }
      public class Milk implements MilkProvider { // same as you example }
      

      如果需要另一个自定义实现,您可以这样编写代码:

      public class MyMilk implements MilkProvider {
        private MilkProvider milk; 
      
        public MyMilk(int someValue) {
          milk = new Milk(someValue);  // unfortunatly we can't get rid of a depencency
                                       // to the concrete class because we need to create
                                       // something. An existing factory could help, but
                                       // but usually there's none implemented.
        }
      
        public float getMilk() {
          float result = milk.getMilk();
          // do somethink with the result
          return float;
        }
      }
      

      【讨论】:

      • 我确实说过它们都不是接口,但是您是否建议我为要使用的类创建一个接口,然后创建我的类并从中实现?我真的不想弄乱我导入的类。我确实在原始问题中提到了扩展与实现。
      • 不,我添加虚拟界面只是为了演示目的。您可以从类声明中删除 MilkProvider 接口并将private MilkProvider milk; 更改为private Milk milk;。但是如果你设计了一些新的东西,那么你应该创建接口。
      【解决方案4】:

      如果它们是公共的,您可以通过方法访问器(getter 和 setter)扩展类和访问实例变量。

      您可以使用AOP (Aspect Oriented Programming) 在运行时更改您的 moo 类,而无需更改其源代码。

      考虑也读一些Composition vs. Inheritance topics

      希望这会对你有所帮助。

      【讨论】:

      • 谢谢,我去看看。
      【解决方案5】:

      我认为在这种情况下更好的解决方案将是多态性(静态多态性),或者您可以使用反射(不要使用这种方式)来达到私有变量。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-22
        • 1970-01-01
        • 2022-11-30
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多