Java 名称隐藏: 艰难之路

我有个名字隐藏的问题,非常难解决。这里有一个简化版本来解释这个问题:

有一个类: 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(); 但是这个类只会因为名称冲突而存在,这看起来非常混乱!有没有可以直接从 B.doSomething()调用 X.doSomething()的解决方案?

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

4886 次浏览

Not really THE answer but you could create an instance of X and call the static method on it. That would be a way (dirty I admit) to call your method.

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

You can use a static import:

import static net.foo.X.doSomething;


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

Watch out that B and A do not contain methods named doSomething

The proper way of doing things would be the static import, but in the absolute worst case scenario, you COULD construct an instance of the class using reflection if you know its fully qualified name.

Java: newInstance of class that has no default constructor

And then invoke the method on the instance.

Or, just invoke the method itself with reflection: Invoking a static method using reflection

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

Of course, these are obviously last resorts.

Probably the simplest (not necessarily the easiest) way to manage this would be with a delegate class:

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

and then ...

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

This is somewhat verbose, but very flexible - you can get it to behave any way you want; plus it works in much the same way both with static methods and instantiated objects

More about delegate objects here: http://en.wikipedia.org/wiki/Delegation_pattern

You can cast a null to the type and then invoke the method on that (which will work, since the target object isn't involved in invocation of static methods).

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

This has the benefits of

  • being side-effect free (a problem with instantiating net.foo.X),
  • not requiring renaming of anything (so you can give the method in B the name you want it to have; that's why a import static won't work in your exact case),
  • not requiring the introduction of delegate class (though that might be a good idea…), and
  • not requiring the overhead or complexity of working with the reflection API.

The downside is that this code is really horrible! For me, it generates a warning, and that's a good thing in general. But since it's working around a problem that is otherwise thoroughly impractical, adding a

@SuppressWarnings("static-access")

at an appropriate (minimal!) enclosing point will shut the compiler up.

This is one of the reasons that composition is preferable to inheritance.

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();
}
}

There's no need to do any cast or suppress any strange warnings or create any redundant instance. Just a trick using the fact that you can call parent class static methods via the sub-class. (Similar to my hackish solution here.)

Just create a class like this

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

(The private constructor in this final class makes sure no one can accidentally create an instance of this class.)

Then you're free to call X.doSomething() via it:

    public class B extends A {


public void doSomething() {
XX.doSomething();
}

What if you try getting the gobalnamespace given all the files are in the same folder. (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...


}
}

I would use the Strategy pattern.

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();
}
}

Now you have also decoupled your dependency, so your unit tests will be easier to write.