97

我有一个非常难以解决的名称隐藏问题。这是解释问题的简化版本:

有一个类:org.A

package org;
public class A{
     public class X{...}
     ...
     protected int net;
}

然后有一堂课net.foo.X

package net.foo;
public class X{
     public static void doSomething();
}

现在,这是继承自A并想要调用的有问题的类net.foo.X.doSomething()

package com.bar;
class B extends A {

    public void doSomething(){
        net.foo.X.doSomething(); // doesn't work; package net is hidden by inherited field
        X.doSomething(); // doesn't work; type net.foo.X is hidden by inherited X
    }
}

如您所见,这是不可能的。我不能使用简单名称X,因为它被继承的类型隐藏。我不能使用完全限定名称net.foo.X,因为net它被继承的字段隐藏。

只有类B在我的代码库中;类net.foo.Xorg.A是库类,所以我不能改变它们!

我唯一的解决方案是这样的:我可以调用另一个类,然后调用X.doSomething(); 但是这个类只会因为名字冲突而存在,看起来很乱!有没有我可以直接调用X.doSomething()的解决方案B.doSomething()

在允许指定全局命名空间的语言中,例如,global::在 C# 或::C++ 中,我可以简单地net使用这个全局前缀作为前缀,但 Java 不允许这样做。

4

9 回答 9

85

您可以将 a 强制null转换为类型,然后在其上调用方法(这将起作用,因为目标对象不参与静态方法的调用)。

((net.foo.X) null).doSomething();

这有以下好处

  • 没有副作用(实例化的问题net.foo.X),
  • 不需要重命名任何东西(因此您可以B以您想要的名称给出方法;这就是为什么 aimport static在您的确切情况下不起作用的原因),
  • 不需要引入委托类(尽管这可能是个好主意……),并且
  • 不需要使用反射 API 的开销或复杂性。

缺点是这段代码真的太可怕了!对我来说,它会产生警告,总的来说这是一件好事。但是由于它正在解决一个完全不切实际的问题,因此添加一个

@SuppressWarnings("static-access")

在适当的(最小的!)封闭点将关闭编译器。

于 2014-07-04T13:14:24.723 回答
38

可能最简单(不一定是最简单)的管理方法是使用委托类:

import net.foo.X;
class C {
    static void doSomething() {
         X.doSomething();
    }
}

接着 ...

class B extends A {
    void doX(){
        C.doSomething();
    }
}

这有点冗长,但非常灵活——你可以让它以任何你想要的方式运行;加上它在static方法和实例化对象上的工作方式几乎相同

更多关于委托对象的信息:http ://en.wikipedia.org/wiki/Delegation_pattern

于 2014-07-04T11:09:33.183 回答
37

您可以使用静态导入:

import static net.foo.X.doSomething;

class B extends A {
    void doX(){
        doSomething();
    }
}

注意,B不要A包含命名的方法doSomething

于 2014-07-04T10:43:07.023 回答
16

做事的正确方法是静态导入,但在绝对最坏的情况下,如果你知道它的完全限定名,你可以使用反射构造一个类的实例。

Java:没有默认构造函数的类的newInstance

然后调用实例上的方法。

或者,只需使用反射调用方法本身: 使用反射调用静态方法

Class<?> clazz = Class.forName("net.foo.X");
Method method = clazz.getMethod("doSomething");
Object o = method.invoke(null);

当然,这些显然是最后的手段。

于 2014-07-04T10:58:26.287 回答
6

不是真正的答案,但您可以创建 X 的实例并在其上调用静态方法。那将是调用您的方法的一种方式(我承认很脏)。

(new net.foo.X()).doSomething();
于 2014-07-04T10:43:01.163 回答
4

无需进行任何强制转换或抑制任何奇怪的警告或创建任何冗余实例。只是一个技巧,您可以通过子类调用父类静态方法。(类似于我在这里的骇人听闻的解决方案。)

只需创建一个这样的类

public final class XX extends X {
    private XX(){
    }
}

(这个最终类中的私有构造函数确保没有人可以意外地创建这个类的实例。)

然后你可以X.doSomething()通过它自由地打电话:

    public class B extends A {

        public void doSomething() {
            XX.doSomething();
        }
于 2014-07-05T17:24:45.293 回答
2

如果考虑到所有文件都在同一个文件夹中,您尝试获取 gobalnamespace 会怎样。(http://www.beanshell.org/javadoc/bsh/class-use/NameSpace.html

    package com.bar;
      class B extends A {

       public void doSomething(){
         com.bar.getGlobal().net.foo.X.doSomething(); // drill down from the top...

         }
     }
于 2014-07-08T08:30:44.723 回答
1

这是组合优于继承的原因之一。

package com.bar;
import java.util.concurrent.Callable;
public class C implements Callable<org.A>
{
    private class B extends org.A{
    public void doSomething(){
        C.this.doSomething();
    }
    }

    private void doSomething(){
    net.foo.X.doSomething();
    }

    public org.A call(){
    return new B();
    }
}
于 2014-07-04T16:36:34.547 回答
0

我会使用策略模式。

public interface SomethingStrategy {

   void doSomething();
}

public class XSomethingStrategy implements SomethingStrategy {

    import net.foo.X;

    @Override
    void doSomething(){
        X.doSomething();
    }
}

class B extends A {

    private final SomethingStrategy strategy;

    public B(final SomethingStrategy strategy){
       this.strategy = strategy;
    }

    public void doSomething(){

        strategy.doSomething();
    }
}

现在你也解耦了你的依赖,所以你的单元测试会更容易写。

于 2014-07-15T11:02:58.207 回答