2011-02-14 49 views
3

我有很多代码调用Foo上的静态方法,如“Foo.method()”。我有两个不同的Foo实现,并且想根据具体情况使用其中一个或另一个。在伪代码:是否有可能使用相同的名称在Java中加载两个不同的类中的一个?

文件Foo1.java

class Foo1 implements Foo { 
    public static int method() { 
    return 0; 
    } 
} 

文件Foo2.java

class Foo2 implements Foo { 
    public static int method() { 
    return 1; 
    } 
} 

文件Main.java

if(shouldLoadFoo1()) { 
    Foo = loadClass("Foo1"); 
} else { 
    Foo = loadClass("Foo2"); 
} 

这可能与Java的元编程?我无法把所有的动态类加载文档包裹起来。如果没有,做我想做的事情的最佳方式是什么?

+0

为什么需要动态加载类,而不是仅创建Foo1和Foo2的单独实例? – 2011-02-14 23:35:30

+0

我猜他的问题在于Foo1和Foo2的方法是静态的。这有点奇怪,对同一个操作有不同的实现通常意味着他不应该使用静态方法,但很难判断这个通用的例子。史蒂夫,这些课程实际上做了什么?如果他们的合同是一样的,为什么你不使用接口? – PaoloVictor 2011-02-14 23:42:29

+0

你确定`method()是静态的吗?它从提供的代码看起来并不那样。 – CurtainDog 2011-02-14 23:54:17

回答

3

本质上你有两个具有相同接口但不同实现的类,使用接口做它会更好吗?

在你的主类,这取决于你将使用适当的实例构造你的类的情况。

FooInterface foo; 
MainClass (FooInteface foo, other fields) { 
    this.foo = foo; 
} 


.... 

然后就从他们使用foo。

另一种方法是用AspectJ,在每次通话Foo.method定义一个切入点,在为切入点的建议有你if (shouldLoadFoo1()) { Foo1.method()}等。

2

可以使用工厂模式来做到这一点。

static Foo makeMeAFoo() 
{ 
    final Foo foo; 
    if(shouldLoadFoo1()) { 
    foo = new Foo1(); 
    } else { 
    foo = new Foo2(); 
    } 
    return foo; 
} 

我在想你在问什么。尽管我更喜欢hhafez的建议。 (注意我的答案现在是OBE b/c提问者将方法转换为静态方法而不是实例方法,但其他答案者的语气很好......通过显式类加载解决这个问题仅仅是因为你想要选择特定的静态方法是一个混乱。)

2

从语言学的角度来看,你写的东西没有任何意义。 Foo是一种类型,并且类型不是变量,不能出现在作业的LHS上。您不能将类型视为Java中的值...该语言不允许它。

,你可以得到什么,你正在尝试做的最接近的是这样的:

Class fooClass; 
if (loadFoo1) { 
    fooClass = Class.forName("some.pkg.Foo1"); 
} else { 
    fooClass = Class.forName("some.pkg.Foo2"); 
} 
Foo foo = (Foo) fooClass.newInstance(); // using the no-args constructor 

(我已经离开了异常处理...)

注意fooClass会是Class类的一个实例,它提供用于反射执行操作的运行时句柄。我们实际上并没有分配类型。我们以有限的方式分配一个“表示”类型的对象。


无论其 ...如果你不这样做需要使用动态加载,你不应该使用它。换句话说,如果你正试图解决的根本问题是创建类的实例,可能静态加载,那么最好使用工厂模式;例如,请参阅@ andersoj的答案。


UPDATE

我只是想出了什么你可能想在这里做。也就是说,您正试图找出一种方法来在不同的静态方法(即Foo1.method()Foo2.method())之间进行选择,而无需在进行调用的位置明确指定类。

同样,你正在尝试做的,根本不会在Java中工作:

  • 你不能声明一个接口的静态方法。
  • 您不能通过接口在实现类中调用静态方法。
  • 静态方法调用不在Java中“调度”。它们是静态绑定的。

有一种方法可以使用反射来做类似这样的事情;例如

Class fooClass; 

// Load one or other of the classes as above. 

Method m = fooClass.getDeclaredMethod("method"); 
Integer res = (Integer) m.invoke(null); 

(和以前一样,我已经离开了异常处理)

再次,你会好得多这样做,而不诉诸动态加载和思考。简单的方法是在一些公用事业类来创建这样一个辅助方法:

public static int method() { 
    return useFoo1 ? Foo1.method() : Foo2.method(); 
} 

更妙的是,这样做的OO方式:在Foo接口作为实例方法声明method,创建注入了一单或例如Foo1Foo2,并依赖于多态性。

但带走的是,有没有办法避免变化的所有地方method()被称为...如果你希望能够在运行时Foo1.methodFoo2.method之间做出选择在你的代码的地方。

2

交换实现的典型方法是使用非静态方法和多态,通常使用依赖注入来告诉依赖代码使用的实现。

下一个干净的方式是单例模式,即宣布:

public abstract class Foo { 
    protected abstract void doSomeMethod(); 

    // populated at startup using whatever logic you desire 
    public static Foo instance; 

    public static void someMethod() { 
     instance.doSomeMethod(); 
    } 
} 

解决你的问题是你要的是什么,即对同一类两种不同的类文件的真正哈克的方式,并在运行时决定使用哪一个。要做到这一点,您需要将项目分为4个不同的jar文件:

  • loader.jar确定要使用的类路径并构造实际应用程序的类加载器。 loader.jar中的类不能引用Foo。
  • foo1。罐子,其中包含富
  • foo2.jar一个包含实现Foo的
  • common.jar另一种实现方式,它包含一切

Loader.jar随后将包含类似的引导方法:

void bootstrap() { 
    URL commonUrl = // path to common.jar 
    URL fooUrl; 
    if (shouldUseFoo1()) { 
     fooUrl = // path to Foo1.jar 
    } else { 
     fooUrl = // path fo Foo2.jar 
    } 
    URL[] urls = {fooUrl, commonUrl}; 
    ClassLoader loader = new UrlClassLoader(urls); 
    Class<?> mainClass = loader.loadClass("my.main"); 
    mainClass.newInstance(); // start the app by invoking a constructor 
} 
1

在你的例子中,你实际上并没有两个不同版本的Foo类,但是两个不同的接口Foo实现,在大多数情况下都很好。 (它们甚至可以彼此平行存在)。

可以加载多个同名的类,但它们必须由不同的类加载器加载。这也意味着你不能有第三个类通过名字引用它,然后使用其中一个或另一个(没有第三个类也在两个类加载器上)。

有时,对于使用不同配置的类(具有相同的外部接口),可能是明智的做法(例如“在客户端”/“在服务器端”,当某些常见类在两个模块都依赖于它),并且在极少数情况下,您将同时在同一个虚拟机中使用两个模块 - 但在大多数情况下,最好使用“一个接口和多个实现类”方法。

2

我不确定我完全理解这里的问题(我看到很多人有这个问题),但让我试着帮忙。 如果您的问题只是使用适当的函数method(),您可以创建一个实用程序函数,根据给定类的实例将调用适当的方法,例如,

否则,我同意斯蒂芬C:“好吧,看我的答案那么这就是你很可能在Java中得到最接近的。”

相关问题