Java名称隐藏:艰难之路
我有一个名字隐藏的问题,这是非常难以解决的。以下是解释问题的简化版本: 有一个类:Java名称隐藏:艰难之路,java,name-clash,member-hiding,Java,Name Clash,Member Hiding,我有一个名字隐藏的问题,这是非常难以解决的。以下是解释问题的简化版本: 有一个类: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,并希
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()
如你所见,这是不可能的。我不能使用简单名称X
,因为它被继承的类型隐藏。我不能使用完全限定名net.foo.X
,因为net
被继承字段隐藏
只有类B
在我的代码库中;类net.foo.X
和org.A
是库类,所以我不能修改它们
我唯一的解决方案如下所示:
我可以调用另一个类,它依次调用X.doSomething()
;但是这个类只会因为名称冲突而存在,这看起来非常混乱!没有解决方案可以直接从B.doSomething()
调用X.doSomething()
<>在允许指定全局命名空间的语言中,例如C++中的代码> Global::/Cuff>:在C++中,>代码> C++,我可以用这个全局前缀简单地前缀<代码> NET<代码>,但是java不允许。不是真正的答案,但是您可以创建X的实例并调用静态方法。 这将是一种(我承认是肮脏的)调用您的方法的方式
(new net.foo.X()).doSomething();
您可以使用静态导入:
import static net.foo.X.doSomething;
class B extends A {
void doX(){
doSomething();
}
}
注意
B
和A
不包含名为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
}
}
然后在实例上调用该方法
或者,只需通过反射调用方法本身:
Class clazz=Class.forName(“net.foo.X”);
方法方法=clazz.getMethod(“doSomething”);
objecto=method.invoke(null);
当然,这些显然是最后的手段。可能最简单(不一定是最简单)的管理方法是使用委托类:
import net.foo.X;
class C {
static void doSomething() {
X.doSomething();
}
}
然后
class B extends A {
void doX(){
C.doSomething();
}
}
这有点冗长,但非常灵活——你可以让它以任何你想要的方式运行;此外,它的工作方式与静态方法和实例化对象的工作方式基本相同
关于委托对象的更多信息:您可以对该类型强制转换一个null
,然后调用该类型上的方法(这将起作用,因为目标对象不涉及静态方法的调用)
这具有以下优点:
- 无副作用(实例化
net.foo.X
时出现问题)
- 不需要重命名任何内容(因此您可以在
B
中为方法指定您想要的名称;这就是为什么import static
在您的情况下不起作用的原因)
- 不需要引入委托类(尽管这可能是个好主意…),以及
- 不需要处理反射API的开销或复杂性
缺点是这段代码非常可怕!对我来说,这会产生一个警告,总的来说这是一件好事。但由于它正在解决一个在其他方面完全不切实际的问题,因此添加
@SuppressWarnings("static-access")
在适当的(最小的!)封闭点将关闭编译器。这是组合优于继承的原因之一
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();
}
}
package.com.bar;
导入java.util.concurrent.Callable;
公共类C实现了可调用
{
私有类B扩展了org.A{
公共无效剂量测定法(){
C.这是做某事的方法;
}
}
私人无效剂量(){
net.foo.X.doSomething();
}
公共组织A call(){
返回新的B();
}
}
无需执行任何强制转换或抑制任何奇怪的警告或创建任何冗余实例。这只是一个技巧,它利用了一个事实,即您可以通过子类调用父类静态方法。(类似于我的黑客解决方案。)
创建一个这样的类
public final class XX extends X {
private XX(){
}
}
(最后一个类中的私有构造函数确保没有人会意外地创建该类的实例。)
然后您可以通过它自由调用X.doSomething()
:
public class B extends A {
public void doSomething() {
XX.doSomething();
}
如果所有文件都在同一个文件夹中,那么尝试获取gobalnamespace会怎么样。()
我会使用策略模式
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();
}
}
现在,您还解耦了依赖项,因此单元测试将更容易编写。一些快速修复程序可能是这样的:public void help(net.foo.X){X.doSomething();}
并使用help(null)调用代码>@荒谬的头脑:嗯,通过一个不存在的对象调用静态方法似乎比我的解决方案更混乱:)。我甚至会收到编译器警告。但确实,这将是除我之外的另一个黑客解决方案。@JamesB:这将实例化错误的Xnet.foo.X
有方法,而不是org.A.X
!您必须从A
继承吗?继承可能非常糟糕,正如您所发现的…我可以调用另一个类,该类反过来调用X.doSomething();但是这个类只会因为名称冲突而存在,这对于干净代码的态度来说似乎非常混乱。但对我来说,这似乎是一种情况,你应该做一个权衡。简单地这样做,并抛出一个很长的关于为什么你必须这样做的评论(可能有一个到这个问题的链接)。net.foo.X.doSomething不是只有包访问权限吗?这意味着您无法从包com访问它。bar@JamesB是的,但是这样的话,完整的问题就没有意义了,因为这个方法无论如何都是不可访问的。我认为在简化示例时这是一个错误,但最初的问题似乎积极希望使用doSomething
作为B
中的方法名称。…@donalvellows:你是对的;如果我的代码为h,则此解决方案不起作用
public class B extends A {
public void doSomething() {
XX.doSomething();
}
package com.bar;
class B extends A {
public void doSomething(){
com.bar.getGlobal().net.foo.X.doSomething(); // drill down from the top...
}
}
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();
}
}