Java名称隐藏:艰难的方式

gex*_*ide 96 java name-clash member-hiding

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

有一节课: org.A

package org;
public class A{
     public class X{...}
     ...
     protected int net;
}
Run Code Online (Sandbox Code Playgroud)

然后是一堂课 net.foo.X

package net.foo;
public class X{
     public static void doSomething();
}
Run Code Online (Sandbox Code Playgroud)

而现在,这是一个有问题的类,它继承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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Don*_*ows 84

您可以将a null转换为该类型,然后在该方法上调用该方法(这将起作用,因为目标对象不涉及静态方法的调用).

((net.foo.X) null).doSomething();
Run Code Online (Sandbox Code Playgroud)

这有好处

  • 没有副作用(实例化的问题net.foo.X),
  • 不需要重命名任何东西(所以你可以按照B你想要的名称给出方法;这就是为什么import static在你的确切情况下不起作用的原因),
  • 不需要引入代表类(虽然这可能是个好主意......),以及
  • 不需要使用反射API的开销或复杂性.

缺点是这段代码非常可怕!对我来说,它产生一个警告,这是一件好事.但是因为它解决了一个原本不切实际的问题,所以添加一个

@SuppressWarnings("static-access")
Run Code Online (Sandbox Code Playgroud)

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


blg*_*lgt 38

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

import net.foo.X;
class C {
    static void doSomething() {
         X.doSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后 ...

class B extends A {
    void doX(){
        C.doSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

这有点冗长,但非常灵活 - 你可以让它以你想要的任何方式行事; 加上它与static方法和实例化对象的工作方式大致相同

有关委托对象的更多信息,请访问:http://en.wikipedia.org/wiki/Delegation_pattern


Abs*_*ind 37

您可以使用静态导入:

import static net.foo.X.doSomething;

class B extends A {
    void doX(){
        doSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

注意B并且A不包含命名的方法doSomething

  • 为了让其他人清楚明白:在继承层次结构中的任何地方都有"doSomething"时,此解决方案不起作用(由于方法调用目标的解析方式).如果你想调用`toString`方法,Knuth会帮助你.Donal提出的解决方案是Bloch的Java Puzzlers书中的解决方案(我认为,没有查找过),所以我们可以认为它是权威的答案:-) (4认同)
  • @DonalFellows:你是对的; 如果我的代码必须像我发布的那样保留,则此解决方案不起作用.幸运的是,我可以重命名该方法.但是,想想我的方法覆盖另一个方法的情况,然后我不能重命名它.在这种情况下,这个答案确实无法解决问题.但这比我已经存在的问题更加巧合,所以也许永远不会有任何人会遇到三重名称冲突的问题:). (3认同)
  • @JamesB是的,但是完整的问题没有意义,因为无法以任何方式访问该方法.我认为这是一个错误,同时简化了示例 (2认同)

Epi*_*rce 16

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

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

然后在实例上调用该方法.

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

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

当然,这些显然是最后的度假胜地.

  • 你应该得到这个答案的完成主义徽章; 你为那个必须使用古老版本的Java并解决这个问题的那个奇怪的穷人提供了答案. (3认同)
  • 到目前为止,这个答案是迄今为止最大的矫枉过正 - 竖起大拇指:) (2认同)

Mic*_*gue 6

不是答案,但你可以创建一个X实例并在其上调用静态方法.这将是一种方式(脏我承认)来调用你的方法.

(new net.foo.X()).doSomething();
Run Code Online (Sandbox Code Playgroud)

  • 将`null`转换为该类型,然后在其上调用该方法会更好,因为创建一个对象可能会产生我不想要的副作用. (3认同)