简体   繁体   English

是否可以使用相同的名称加载Java中的两个不同类中的一个?

[英]Is it possible to load one of two different classes in Java with the same name?

I have a lot of code that calls static methods on Foo like "Foo.method()". 我有很多代码在Foo上调用静态方法,如“Foo.method()”。 I have two different implementations of Foo and would like to use one or the other depending on the circumstances. 我有两种不同的Foo实现,并希望根据具体情况使用其中一种。 In psuedocode: 在伪代码中:

File Foo1.java 文件Foo1.java

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

File Foo2.java 文件Foo2.java

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

File Main.java 文件Main.java

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

Is this possible with Java metaprogramming? 这可能与Java元编程有关吗? I can't quite wrap my head around all the dynamic class loading documentation. 我无法完全围绕所有动态类加载文档。 If not, what's the best way to do what I'm trying to do? 如果没有,那么做我想做的最好的方法是什么?

Essentially you have two classes with the same interface but different implementations,Wouldn't it be better to do it using an interface? 基本上你有两个具有相同接口但实现不同的类,使用接口做它不是更好吗?

in your main class, depending on the circumstances you would construct your class with the appropriate instance. 在您的主类中,根据您使用适当实例构建类的环境。

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


....

then just use foo from them on. 然后只使用他们的foo。

Another way is to use AspectJ, define a point cut on every Foo.method call, in in the advice for the point cut have your if (shouldLoadFoo1()) { Foo1.method()} etc .. 另一种方法是使用AspectJ,在每个Foo.method调用上定义一个切点,在切点的建议中有你的if (shouldLoadFoo1()) { Foo1.method()}等等。

You can use a factory pattern to do this. 您可以使用工厂模式执行此操作。

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

Which is I think what you're asking for. 我觉得你在想什么。 Though I like hhafez' suggestion better myself. 虽然我更喜欢哈菲兹的建议。

(Note my answer is now OBE b/c the questioner shifted the methods to be static rather than instance methods. Nevertheless, the tone of other answerers is good... solving this problem by explicit classloading just because you want to select specific static methods is a kludge.) (注意我的答案现在是OBE b / c,提问者将方法转移到静态而不是实例方法。然而,其他回答者的语气很好......通过显式类加载解决这个问题只是因为你想选择特定的静态方法是一个kludge。)

What you have written doesn't make sense from a linguistic standpoint. 从语言学的角度来看,你所写的内容没有意义。 Foo is an type, and a type is not a variable and cannot appear on the LHS of an assignment. Foo是一种类型,类型不是变量,不能出现在赋值的LHS上。 You cannot treat a type as a value in Java ... the language doesn't allow it. 您不能将类型视为Java中的值...语言不允许它。

The closest that you can get to what you are trying to do is something like this: 你可以得到的最接近你想要做的是这样的:

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

(I've left out the exception handling ...) (我遗漏了异常处理......)

Note that fooClass will be an instance of the class Class which provides runtime handles that are used for performing operations reflectively. 请注意, fooClass将是类Class一个实例,它提供用于反射执行操作的运行时句柄。 We are NOT actually assigning a type. 我们实际上并没有分配类型。 We are assigning an object that "denotes" a type ... in a limited fashion. 我们以有限的方式分配一个“表示”类型的对象。


HOWEVER ... if you don't need to use dynamic loading you should not use it. 但是 ......如果你不需要使用动态加载,你就不应该使用它。 In other words, if the underlying problem that you are trying to solve is creating instances of classes that could be statically loaded, then it is better to use the factory pattern; 换句话说,如果你正在试图解决的是创造 可能被静态加载的类的实例的根本问题,那么最好是使用工厂模式; see @andersoj's answer for example. 以@ andersoj的答案为例。


UPDATE UPDATE

I just figured out what you are probably trying to do here. 我只是弄清楚你可能在这里尝试做什么。 That is, you are trying to figure out a way to choose between different static methods (ie Foo1.method() and Foo2.method() ) without explicitly naming the classes at the point where the call is made. 也就是说,您试图找出一种在不同静态方法(即Foo1.method()Foo2.method() )之间进行选择的方法,而无需Foo2.method()调用时明确命名类。

Again, what you are trying to do simply won't work in Java: 同样,你想要做的只是在Java中不起作用:

  • You cannot declare a static method in an interface. 您不能在接口中声明静态方法。
  • You cannot call a static method in an implementation class via the interface. 您不能通过接口在实现类中调用静态方法。
  • Static method calls are not "dispatched" in Java. 静态方法调用不是在Java中“调度”的。 They are bound statically. 他们是静态约束的。

There is a way to do something roughly like this using reflection; 有一种方法可以使用反射大致这样做; eg 例如

Class fooClass;

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

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

(As before, I've left out the exception handling) (和以前一样,我遗漏了异常处理)

Once again you would be much better off doing this without resorting to dynamic loading and reflection. 再一次,如果不采用动态加载和反射,你会做得更好。 The simple approach would be to create a helper method like this in some utilities class: 简单的方法是在某些实用程序类中创建这样的辅助方法:

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

Better still, do it the OO way: declare method in the Foo interface as a instance method, create a singleton or an injected instance of Foo1 or Foo2 , and rely on polymorphism. 更妙的是,这样做的OO方式:申报methodFoo接口作为实例方法,创建一个单独或注入实例Foo1Foo2 ,并依靠多态性。

But the take away is that there is NO WAY to avoid changing all of the places in your codebase where method() is called ... if you want to be able to choose between Foo1.method and Foo2.method at runtime. 但需要注意的是,没有办法避免更改代码库中调用method()位置...如果您希望能够在运行时选择Foo1.methodFoo2.method

The typical approach to exchanging implementations is to use a non-static method and polymorphism, typically using dependency injection to tell the depedent code the implementation to use. 交换实现的典型方法是使用非静态方法和多态,通常使用依赖注入来告知依赖代码要使用的实现。

The next cleanest way is the singleton pattern, ie to declare: 下一个最简洁的方法是单例模式,即声明:

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

The really hacky way to solve your problem would be what you ask for, ie to have two different class files for the same class, and decide at runtime which one to use. 解决问题的真正hacky方法就是你要求的,即为同一个类提供两个不同的类文件,并在运行时决定使用哪一个。 To do that, you would seperate your project into 4 different jar files: 为此,您可以将项目分成4个不同的jar文件:

  • loader.jar that determines the classpath to use and constructs the classloader for the actual application. loader.jar确定要使用的类路径并为实际应用程序构造类加载器。 The classes in loader.jar must not reference Foo. loader.jar中的类不能引用Foo。
  • foo1.jar that contains one implementation for Foo foo1.jar包含一个Foo实现
  • foo2.jar that contains another implementation for Foo foo2.jar包含Foo的另一个实现
  • common.jar that contains everything else common.jar包含其他所有内容

Loader.jar would then contain a bootstrap method like: 然后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
}

I am not sure I fully understand the problem here (I see many has that issue), but let me try to help. 我不确定我是否完全理解这里的问题(我看到很多人都有这个问题),但让我试着帮忙。 If your problem was coming down just to using appropriate function method(), you could create a utility function that depending on an instance of a given class will call appropriate method, eg 如果您的问题只是使用适当的函数方法(),您可以创建一个实用程序函数,根据给定类的实例将调用适当的方法,例如

private static int getResultOfFoo(Foo foo)
{
int res = -1;
if(foo instanceof Foo1)
    res = Foo1.method();
else res = Foo2.method();
return res;
}

Otherwise, I agree with Stephen C: "Well, see my answer then. That's the closest you are likely to get in Java." 否则,我同意Stephen C的观点:“好吧,那么请看我的答案。这是你可能用Java最接近的。”

In your example you in fact have not two different versions of class Foo, but two different implementations of the interface Foo, which is fine in most cases. 在你的例子中,你实际上没有两个不同版本的类Foo,但是两个不同的Foo接口实现,在大多数情况下都没问题。 (They even can exist parallel to each other.) (它们甚至可以彼此平行存在。)

It is possible to load multiple classes of the same name, but they have to be loaded by different classloaders. 可以加载多个同名的类,但它们必须由不同的类加载器加载。 This also means that you can't have a third class referencing it by name and then using one or the other (without the third class also being on two classloaders). 这也意味着你不能让第三个类按名称引用它,然后使用其中一个(没有第三个类也在两个类加载器上)。

Sometimes it may be sensible to have different versions of a class (with same external interface) for different configurations where it would be used (such as "on client side" / "on server side", when some common class in both modules depends on it), and in rare cases you would have both modules in the same VM at the same time - but in most cases it would be better to use the "one interface and multiple implementing classes" approach instead. 有时,为不同的配置使用不同版本的类(具有相同的外部接口)可能是明智的(例如“在客户端”/“在服务器端”,当两个模块中的某些公共类依赖于在极少数情况下,您可以同时在同一个VM中同时使用这两个模块 - 但在大多数情况下,最好使用“一个接口和多个实现类”方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM