我正在从几个不同的开源库构建工具。我的构建路径按以下顺序排列:
我的第一个JAR文件stanford-corenlp-3.3.0.jar
包含一个名为edu.stanford.nlp.process
的包,其中包含Morphology.class
类。
我的第二个JAR文件ark-tweet-nlp-0.3.2.jar
包含相同的包名称(edu.stanford.nlp.process
)和相同的类名Morphology.class
。
在两个JARS中,在各自的Morphology
类中都存在一个名为stem()
的方法。但是,这些方法的构造函数是不同的。我想从我的第二个JAR文件中使用stem(String, String)
方法,但由于import语句(import edu.stanford.nlp.process.Morphology;
)没有指定要使用哪个JAR,因为它认为构建路径上的第一个JAR,我收到错误是我想要实现的那个。
我不想改变构建路径的顺序,因为它会抛弃我的其他方法调用。
如何指定要使用哪个JAR的Morphology
类?是否有一个import语句指定JAR以及package.class?
编辑:如何组合我的两个JAR以便两个Morphology
类合并,给我两个不同构造函数的方法?
答案 0 :(得分:2)
默认ClassLoader
只会加载其中一个广告,而忽略第二个广告,因此无法开箱即用。也许自定义ClassLoader
可以提供帮助。
有关ClassLoader
的详细信息,请从here开始。
编辑:我们正在研究一些可怕的包装选择,这会导致这个Jar Hell的副作用。这个"Ark Twitter"库的作者认为发布包含第三方库(Stanford NLP库)的JAR工件是个好主意。这导致Ark Twitter与其使用的Stanford NLP库的特定版本之间不必要的紧密耦合。这是一种非常糟糕的做法,在任何情况下都应该不鼓励:这违反了关于传递依赖的整个想法。
编辑(续):一个可能(并且希望正在发挥作用)的解决方案是重建Ark Twitter JAR,使其不包含上述库但只包含其自己的库代码(基本上只是cmu.arktweetnlp包)并希望您的项目所需的NLP版本与Ark Twitter一起使用。理想情况下,您应该向库的作者提交拉取请求,但与此同时,您可以通过解除乱码并重新震动现有的JAR文件。
编辑2 :再次查看JAR文件,我原先想到的情况要糟糕得多: ALL 依赖关系在已发布的JAR文件中重新打包。这实际上是发布库的最糟糕的解决方案。祝你好运。
答案 1 :(得分:2)
正如上面其他几位指出的那样,可以调整Java的类加载器机制来从某些地方加载类......但是这不是你想要的,相信我。
你遇到了一个已知问题。您应该考虑使用不同版本的ArkTweet,而不是担心如何告诉Java使用来自一个JAR而不是另一个JAR的类。
获取ArkTweet JAR from Maven Central。它不包含斯坦福大学的课程。
当你注意到人们在他们的JAR中打包第三方课程时,我建议他们指出这通常不是一个好主意,并鼓励他们不要这样做。如果一个项目提供了一个包含所有依赖项的可运行的fat-jar,那很好。但是,它不应该是他们提供的唯一JAR。还应提供没有任何第三方代码的普通JAR或JAR集。在极少数情况下,第三方代码被修改并且必须包含在内,它应该在提供者的包命名空间下完成,而不是在原始第三方的包名称下。
答案 2 :(得分:1)
不,没有。这是Java的一个弱点,无法简单地解决。您应该只使用其中一个库。在类路径中同时使用java将始终选择第一个。
此问题的名称为Jar hell。
答案 3 :(得分:1)
构建路径中的顺序通常决定了类加载器搜索类的顺序。但是,一般情况下,您不希望在构建路径中重复同一个类 - 并且肯定看起来不像ark-tweet-nlp-0.3.2.jar中应该有一个edu.stanford包。
答案 4 :(得分:1)
我认为只需在当前CoreNLP的Morphology类中使用lemma(String word, String tag)
方法即可解决您的问题:
String word = ...;
String tag = ...;
String lemma = morphology.lemma(word, tag);
WordTag wt = new WordTag(lemma, tag);
几年前修改课程时,您要查找的方法已被删除。感觉是,随着大多数斯坦福NLP代码转向使用CoreLabel
s,返回WordTag
的方法不太有用(尽管删除所有这些方法仍然是一项工作正在进行中)。
答案 5 :(得分:0)
当你加载一个类时,它被加载到给定的地址,然后该地址放在从类创建的对象的头中,这样(除其他外)可以找到类中的方法。
因此,如果以某种方式使用方法abc(String)从zip文件XYZ.zip加载ClassA,它将加载到地址12345.然后(使用类加载器技巧)加载另一个ClassA,方法为abc(String,String) ),从zip文件ZYX.zip,并加载到地址67890。
现在创建第一个ClassA的实例。在它的标题中,类地址为12345.如果你可以某种方式尝试在该类上调用方法abc(String,String),那么在12345的类中将找不到该方法。(实际上,你甚至不能尝试调用,因为验证者会阻止你,因为对它来说,这两个类是完全不同的,你试图使用另一个调用另一个类,就好像它们的名字完全不同。)