给Java类库添加功能

7
我正在使用一个Java类库,它在很多方面都不完整:有很多类我觉得应该增加额外的成员函数。然而,我不确定添加这些成员函数的最佳实践方法。
让我们称这个不足的基类为A
class A
{
    public A(/*long arbitrary arguments*/)
    {
        //...
    }

    public A(/*long even more arbitrary arguments*/)
    {
        //...
    }

    public int func()
    {
        return 1;
    }
}

理想情况下,我想给A添加一个功能。但是我不能这样做。我的选择在于:

class B extends A
{
    //Implement ALL of A's constructors here

    public int reallyUsefulFunction()
    {
        return func()+1;
    }
}

并且

class AddedFuncs
{
    public static int reallyUsefulFunction(A a)
    {
        return a.func()+1;
    }
}

我认为它们都有优缺点。第一种选择比第二种选择具有更干净的语法,更加合理,但是存在问题:假设我在类库中有第三个类C
class C
{
    public A func()
    {
        return new A(/*...*/);
    }
}

我认为,没有简单的方法可以做到这一点:


C c;
int useful = c.func().reallyUsefulFunction();

因为 C.func() 返回的类型是 A,而不是 B,所以你无法进行向下转型。

那么,在只读库类中添加成员函数的最佳方法是什么?


1
@Eric:通常在Java中,即使它们是函数,函数也被称为“方法” :) - SyntaxT3rr0r
4个回答

2
为什么不使用组合而不是继承?
class ABetterA {
    private A a;

    public ABetterA() {

    }

    // write wrapper methods calling class' A methods and maybe doing something more
}

这样,您还可以模拟多重继承...

2
自然而常见的困境。阅读关于组合 vs 继承的替代方案。你的第二个选择基本上是一种组合,如果我们认为对象A是在构造函数中传递而不是在每个方法中传递 - 也就是说,我们将使用组合来实现包装器装饰者模式。
对于类C返回类A的新实例问题,没有一个简单的解决方案,正如你猜测的那样,只要类C决定负责创建新实例。这就是为什么在类内部输入“new”语句之前,应该暂停和思考,如果有可能这个类会被子类化。在您的示例中,如果您可以告诉类C要返回哪个具体类,那就太好了...但是它怎么知道要创建它呢?嗯,我们可以给他传递一个知道如何实例化类A(或其子类)的对象...我想你已经足够有动力去阅读工厂模式和设计模式了。
没有唯一最佳答案,但如果想要一个快速的答案:我会创建一个包装器,B类不继承A,但有一个以A为参数的构造函数,它将其方法(除了自己的方法)委托给内部对象。 当你需要调用C类中的方法时(我假设你不能修改C类),你可以这样写:B b = new B(c.func())

我唯一能接触类C的方式是从它派生出另一个修改后的类,这样做很快就会使代码难以阅读。有没有一种简单的方法让B扩展A,并给它一个以A为参数的构造函数? - Eric
不,你在这里混淆了事情(确切地说是组合和继承)。继承和装饰器(通过组合实现)是你可以“接触”类的两种替代方式(即将行为状态添加到它)。如果你阅读相关资料,你会发现那些刚接触面向对象编程的人经常高估继承的重要性。 - leonbloy

1

你有第三种选择。你可以使用Scala(一种与Java兼容的语言)和它的特质,这些特质是另一个名字的mixins


很遗憾,我没有那个选项。我正在使用机器人平台(LeJOS NXJ)的Java子集。 - Eric

0

除了Brian的建议之外,另一个类似的选择是使用面向切面编程(AOP)工具,例如AspectJ,它可以让您将附加功能“注入”到现有类中,甚至是二进制类。您可以预处理库jar文件以获取具有增强类的新文件(“静态编织”),或者在加载库类时进行所有操作(所谓的“加载时编织”)。您可以查看此AspectJ示例

尽管AOP通常用于修改现有方法(前置、后置或环绕“建议”=代码片段),但您也可以引入新成员和方法-请查看AspectJ的接口类型声明

当然,问题在于您的受限平台是否支持AspectJ。


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