为什么我不能“静态导入” “等于” Java 中的方法?

发布于 2024-12-11 23:59:50 字数 996 浏览 0 评论 0原文

我喜欢在这里使用这个方法:

org.apache.commons.lang.ObjectUtils.equals(Object object1, Object object2)

唯一的缺点(例如与 Google Guava 相比)是我无法静态导入该方法。即,这是无用的:

import static org.apache.commons.lang.ObjectUtils.equals;

...因为我的 Eclipse 编译器在编写时不会正确链接该方法

equals(obj1, obj2);

错误是:

Object 类型中的方法 equals(Object) 不适用于参数 (..., ...)

这是为什么?如果任何超类型中存在具有相同名称(但签名不同)的方法,我的静态导入方法是否不适用? JLS 中有正式规定吗? 或者某些 Eclipse 编译器问题?

更新

这也不起作用:

import static org.apache.commons.lang.ObjectUtils.defaultIfNull;

public class Test {
  void test() {
    defaultIfNull(null, null);
    // ^^ compilation error here
  }

  void defaultIfNull() {
  }
}

javac 错误消息:

Test.java:5: defaultIfNull() in Test cannot be applied to (<nulltype>,<nulltype>)
defaultIfNull(null, null);
    ^
1 error

I like using this method here:

org.apache.commons.lang.ObjectUtils.equals(Object object1, Object object2)

The only drawback (compared to Google Guava, for instance), is that I cannot static import the method. I.e. this is useless:

import static org.apache.commons.lang.ObjectUtils.equals;

... as my Eclipse compiler will not correctly link that method when writing

equals(obj1, obj2);

The error is:

The method equals(Object) in the type Object is not applicable for the arguments (..., ...)

Why is that? Is my statically imported method not applicable if there is a method with the same name (but not the same signature) in any of the super types? Is this formally specified in the JLS? Or some Eclipse compiler issue?

UPDATE

This doesn't work either:

import static org.apache.commons.lang.ObjectUtils.defaultIfNull;

public class Test {
  void test() {
    defaultIfNull(null, null);
    // ^^ compilation error here
  }

  void defaultIfNull() {
  }
}

javac error message:

Test.java:5: defaultIfNull() in Test cannot be applied to (<nulltype>,<nulltype>)
defaultIfNull(null, null);
    ^
1 error

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

飘逸的'云 2024-12-18 23:59:50

碰撞实际上是与Object.equals() 发生的。所有类均继承自 Object,因此具有导致此冲突的 Object.equals() 方法。

您是按姓名导入,而不是按签名导入。因此,您实际上无法导入名为 equals 的静态方法。或者更确切地说,您可以导入它,但不能使用它。我确实同意这应该有效。

(我的评论是我自己的答案。)

The collision is actually with Object.equals(). All classes are inherited from Object and therefore have the Object.equals() method which leads to this collision.

You're importing by name, not by signature. You actually can't import a static method named equals because of this. Or rather, you can import it, but not use it. I do agree that this should work though.

(Made my comments my own answer.)

兔小萌 2024-12-18 23:59:50

根据 Java 语言规范

  1. 如果单静态导入声明导入一个简单的成员
    name为n,编译单元还包含single-type-import
    导入简单名称为 n 的类型的声明,编译时
    发生错误。 (即使两个声明都引用,也会发生此错误
    相同类型,因为使用两种不同的会造成混淆
    冗余导入相同类型的机制。)
  2. 如果单静态导入声明导入一个简单的成员
    name为n,编译单元还声明了一个顶级类型
    如果其简单名称为 n,则会发生编译时错误。

因此,在您的情况下,上面提到的第 2 点是您收到编译时错误的原因。因此,即使方法签名不同,如果名称相同,也会出现编译时错误。

静态导入 JSRJLS

As per Java Language Specification

  1. If a single-static-import declaration imports a member whose simple
    name is n, and the compilation unit also contains a single-type-import
    declaration that imports a type whose simple name is n, a compile-time
    error occurs. (This error occurs even if both declarations refer to
    the same type, on the grounds that it is confusing to use two different
    mechanisms to redundantly import the same type.)
  2. If a single-static-import declaration imports a member whose simple
    name is n, and the compilation unit also declares a top level type
    whose simple name is n, a compile-time error occurs.

So in your case its point 2 mentioned above is the reason you are getting compile time error. so even if method signatures are different if there names are same its a compile time error.

static import JSR and JLS

遥远的她 2024-12-18 23:59:50

JLS 15.12.1。确定方法可以“在范围内”的两个原因:

  1. “...存在一个封闭类型声明,该方法是该类型声明的成员”
  2. “...由于一个或多个单静态导入...”

现在有两个因素导致了令人惊讶的结果:

  1. 此时仅考虑方法的名称,签名稍后出现。
  2. 上面提到的两种选择都与“否则”相关。在第一种情况下,我们最终会查找可见方法的封闭类。在第二种情况下,我们使用静态导入。

这个“否则”意味着搜索范围仅限于尝试两个分支中的任何一个。
首先,我们必须决定是搜索封闭类型还是使用静态导入。封闭类型具有更高的优先级,我们找到了正确名称的方法(Test.defaultIfNull()),搜索到此结束。当后来我们发现这个方法不兼容时,就没有办法再尝试静态导入了。

这种情况在 JLS 中并不罕见,方法查找的其他问题也是分阶段组织的,其中一个阶段中的部分匹配可能会妨碍在后续阶段中找到更好的匹配。固定数量与可变数量匹配是这个概念的另一个例子。在所有情况下的效果是编译器不会搜索整个可能的解决方案空间,但在做出某些决定后,整个分支将被切断并且永远不会被访问。

从上面可以得出一个经验法则:重载只能在相同类型层次结构的方法之间进行选择,而不能在继承无关的类型的方法之间进行选择。

JLS 15.12.1. identifies two reasons, why a method can be "in scope":

  1. "...there is an enclosing type declaration of which that method is a member"
  2. "... due to one or more single-static-import ..."

Now two factors contribute to the surprising result:

  1. At this point only the name of the method is considered, signatures come later.
  2. The two alternatives mentioned above are connected with "otherwise". In the first case we end up looking in the enclosing class of the visible method. In the second case we use the static import.

This "otherwise" implies that the search scope is restricted to only trying either of the two branches.
First we have to decide whether we search an enclosing type or using a static import. Enclosing type has higher priority, we find a method of the correct name (Test.defaultIfNull()), search ends here. When later we find this method to be incompatible, there's no going back to trying the static import.

The situation is not uncommon in JLS, also other issues of method lookup are organized in phases, where a partial match in one phase may prevent finding a better match in a subsequent phase. Fixed-arity vs. variable arity matching is another example of this concept. The effect in all cases is that compilers do not search the entire possible solution space, but after certain decisions have been made entire branches are cut off and never visited.

A rule of thumb can be derived from the above: Overloading can only select among methods of the same type hierarchy, it cannot select between methods of types unrelated by inheritance.

陈年往事 2024-12-18 23:59:50

我做了一些测试。我注意到的第一件事是,对于同名的多个方法,您只需要一个静态导入语句。

public class EqualsClass {
  public static boolean equals(Object o1, Object o2) {
    return o1 == null ? o2 == null : o1.equals(o2);
  }

  public static boolean equals(Object o1, Object o2, Object o3) {
    return equals(o1, o2) && equals(o2, o3);
  }
}

import static mypackage.EqualsClass.equals;

public class TestClass {
  public static void main() {
    Object o1 = new Object();
    Object o2 = new Object();

    equals(o1, o2); // Compiles - static context

    Object o3 = new Object();

    equals(o1, o2, o3); // No extra static import required
  }

然后我注意到它在实例上下文中不起作用:

  public void someInstanceMethod() {
    Object o1 = new Object();
    Object o2 = new Object();

    equals(o1, o2); // Does not compile - instance context

    Object o3 = new Object();

    equals(o1, o2, o3); // As expected does not compile
  }

}

但是如果我用类自己的静态方法破坏静态导入:

public static boolean equals(Object o1, Object o2) {
  return EqualsClass.equals(o1, o2); // Compiles
}

public void someInstanceMethod() {
  equals(new Object(), new Object()); // Compiles!!
  equals(new Object(), new Object(), new Object()); // Doesn't compile!
}

它在静态上下文中工作的事实对我来说是很有意义的。但是,静态导入方法和类的已定义静态方法的解析之间似乎存在显着差异。

摘要:

  • 从实例上下文静态导入时,无法访问与实例方法同名的方法。
  • 可以从实例上下文访问具有相同名称的同一类的静态方法。
  • 静态导入使您可以访问该类中具有相同名称的所有静态方法,尽管有签名(参数和返回值)。

我有兴趣查看 JLS 或编译器规范的一部分,该部分指定编译器对静态导入的解析以及它们如何被本地方法破坏。

I did a few tests. First thing I noticed is that you only need one static import statement for multiple methods of the same name.

public class EqualsClass {
  public static boolean equals(Object o1, Object o2) {
    return o1 == null ? o2 == null : o1.equals(o2);
  }

  public static boolean equals(Object o1, Object o2, Object o3) {
    return equals(o1, o2) && equals(o2, o3);
  }
}

import static mypackage.EqualsClass.equals;

public class TestClass {
  public static void main() {
    Object o1 = new Object();
    Object o2 = new Object();

    equals(o1, o2); // Compiles - static context

    Object o3 = new Object();

    equals(o1, o2, o3); // No extra static import required
  }

Then I noticed that it doesn't work in an instance context:

  public void someInstanceMethod() {
    Object o1 = new Object();
    Object o2 = new Object();

    equals(o1, o2); // Does not compile - instance context

    Object o3 = new Object();

    equals(o1, o2, o3); // As expected does not compile
  }

}

But then if I clobber the static import with the class's own static method:

public static boolean equals(Object o1, Object o2) {
  return EqualsClass.equals(o1, o2); // Compiles
}

public void someInstanceMethod() {
  equals(new Object(), new Object()); // Compiles!!
  equals(new Object(), new Object(), new Object()); // Doesn't compile!
}

The fact that it works in a static context makes a reasonable amount of sense to me. However, it appears there is a significant difference between the resolution of a statically imported method and a defined static method of the class.

Summary:

  • Methods with the same name as an instance method cannot be accessed when statically imported from an instance context.
  • Static methods from the same class with the same name can be accessed from an instance context.
  • A static import gives you access to all static methods with the same name from that class despite the signature (parameters and return value).

I'd be interested to see the part of the JLS or a compiler spec that specifies the resolution of static imports by the compiler and how they are clobbered by local methods.

苍白女子 2024-12-18 23:59:50

我也梳理了JLS3,没有找到明确的答案。

根据 15.12.1,首先我们需要确定声明/继承 equals 方法的单个类。这里我们有两个候选类,并且规范似乎没有解决冲突的规则。

我们可以研究一个类似的问题。简单类型名称可以引用导入类型或继承类型(超类的成员类型)。 Javac 选择了后者。这可能是因为 6.5.2 中的程序为导入提供了最低优先级。

如果同样的原则适用,导入的 ObjectUtils.equals 应该屈服于继承的 Object.equals。然后,根据 15.12.2.1,Object 中没有可能适用于表达式 equals(obj1, obj2)equals 方法,

就我个人而言,我' d 更喜欢 import 优先于继承,因为 import 更接近。它还稳定了名字的含义。在当前方案中,假设 Object 没有 equals 方法,则表达式 equals(obj1, obj2) 引用 ObjectUtils .等于;现在假设 Object 添加了 equals 方法,这是一个完全无辜的举动,突然子类无法编译。更糟糕的情况是:新的 equals 方法具有兼容的签名;子类仍然可以编译,但表达式的含义悄然发生了变化。

I also combed through JLS3 and couldn't find a definitive answer.

per 15.12.1, first we need to determine the single class where the equals method is declared/inherited. Here we have two candidate classes, and the spec doesn't seem to have a rule to resolve the conflict.

We can investigate a comparable problem. A simple type name may refer to both an imported type, or an inherited type (a member type of the super class). Javac picks the latter. This is probably because of the procedure in 6.5.2, which gives imports the lowest priority.

If the same principle applies, the imported ObjectUtils.equals should yield to inherited Object.equals. Then per 15.12.2.1, there is no equals method in Object that's potentially applicable to the expression equals(obj1, obj2)

Personally, I'd prefer that import has precedence over inheritance, because import is closer. It also stabilizes the meaning of a name. In the current scheme, suppose Object doesn't have an equals method, the expression equals(obj1, obj2) refers to ObjectUtils.equals; now suppose Object adds the equals method, a totally innocent move, suddenly the subclass doesn't compile. An even worse scenario: the new equals method has a compatible signature; the subclass still compiles, yet the meaning of the expression silently changes.

同尘 2024-12-18 23:59:50

这并不是真正的答案(在某种程度上只是更多问题)。这证明编译器确实导入了带签名的方法。

package test;

public class Foo 
{
    public static void equal(Object o1)
    {
        System.out.println("Foo.equal Object");
    }   

    public static void equal(Integer o1)
    {
        System.out.println("Foo.equal Integer");
    }   
}

package test;

public class Bar 
{
    public static void equal(Number o1)
    {
        System.out.println("Bar.equal Number");
    }   
}

import static test.Foo.equal;
import static test.Bar.equal;

public static void main(String args[]) throws Exception
{
    equal((Object)null);
    equal((Number)null);
    equal((Integer)null);
}

Output: 
Foo.equal Object
Bar.equal Number
Foo.equal Integer

这或许也有关系。内部类中的方法“隐藏”外部类中具有不同签名的静态方法。

http://ideone.com/pWUf1

看起来编译器有不同的位置来查找方法并检查他们一一搜索,但仅按名称进行搜索,导致搜索过早终止。

This isnt really an answer (just more questions in a way). This is proof that the compiler does import the methods with signature.

package test;

public class Foo 
{
    public static void equal(Object o1)
    {
        System.out.println("Foo.equal Object");
    }   

    public static void equal(Integer o1)
    {
        System.out.println("Foo.equal Integer");
    }   
}

package test;

public class Bar 
{
    public static void equal(Number o1)
    {
        System.out.println("Bar.equal Number");
    }   
}

import static test.Foo.equal;
import static test.Bar.equal;

public static void main(String args[]) throws Exception
{
    equal((Object)null);
    equal((Number)null);
    equal((Integer)null);
}

Output: 
Foo.equal Object
Bar.equal Number
Foo.equal Integer

This may also be related. A method in an inner class 'hiding' a static method in the outer class with a different signature.

http://ideone.com/pWUf1

It looks like the compiler has different places where to look for methods and it checks them one by one but only searches by name leading to a premature termination of the search.

计㈡愣 2024-12-18 23:59:50

这是与 java.awt 的方法冲突,您需要像这样引用该包:

 ObjectUtils.equals(a, b);

It's a method collision with java.awt, you need to reference the package like this:

 ObjectUtils.equals(a, b);
仙气飘飘 2024-12-18 23:59:50

事实上,我认为这更多的是 Eclipse 问题而不是其他问题。
如果您使用接收两个参数的 equals() 的重载版本,则不应与默认的 Object.equals() 发生冲突。

Eclipse 中有一些技巧,您可以使用它们来识别静态导入:

1 - 添加静态类型以组织导入
转到:

Window > Preferences > Java > Code Style > Organize Imports 

然后单击“New Static”,然后单击“Types”,然后选择您的类(在本例中为 org.apache.commons.lang.ObjectUtils)

仍在“组织导入”面板上时,取消选择

"Do not create imports for types starting with lowercase letter" 

(不要忘记这一点,这很重要)

2 - 将类型添加到内容辅助
转到:

Window > Preferences > Java > Editor > Content Assist Favorites

然后单击“New Type”,然后选择您的类(在本例中,再次选择 org.apache.commons.lang.ObjectUtils)

现在,您应该能够在方法中的任何位置按 Ctrl+Space 并获得“equals(Object,Object)”方法作为可能的内容。如果您选择该方法,Eclipse 应该自动插入 equals 的静态导入。

Actually I think this is more of an Eclipse issue than any other thing.
If you are using an overloaded version of equals() that receives two arguments, there should be no collision with the default Object.equals().

There are a couple of tricks in Eclipse that you can use to get it to recognize the static import:

1 - Add the static type to Organize Imports
Go to:

Window > Preferences > Java > Code Style > Organize Imports 

then click on "New Static", then "Types", then choose your class (in this case org.apache.commons.lang.ObjectUtils)

While still on the Organize Imports panel, deselect the

"Do not create imports for types starting with lowercase letter" 

(do not forget this, it's important)

2 - Add the type to Content Assist
Go to:

Window > Preferences > Java > Editor > Content Assist Favorites

then click on "New Type", then choose your class (in this case, again, org.apache.commons.lang.ObjectUtils)

Now with this you should be able to Ctrl+Space anywhere on your method and get the "equals(Object,Object)" method as possible content. If you choose that method, Eclipse should automatically insert the static import for equals.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文