有一个不使用实例变量的非静态方法有意义吗?

Does it make sense to have a non static method which does not use an instance variable?

编译器不允许静态方法调用非静态方法。我理解它这样做是因为非静态方法通常最终使用实例变量。

但是拥有一个不使用实例变量的非静态方法是否有意义。如果我们有一个不影响或不受实例状态影响的行为,不应该将这样的方法标记为静态的。

I understand it does this because a not static method usually ends up using an instance variable.

即使实例方法不使用实例变量,它仍然绑定到class的实例。事实上,它在方法参数中隐含地引用了 this

也就是说,在下面的方法中:

public void foo() {

}

this 作为方法中的第一个局部变量隐式传递。

编辑:

重新阅读问题,这是一个更广泛的问题,视情况而定。通常,如果该方法不需要实例(并且您很确定它不需要),那么只需将其设为 static.

很多时候,没有。如果该方法不涉及任何实例状态,则没有理由将其绑定到实例。

当然,静态方法不能被继承或重写,所以这是一个明显的时间,你会想要一个不使用实例状态的实例方法。 strategy pattern 就是一个典型的例子。

另一种情况是,如果这是一个 public API 并且您认为将来可能希望将该方法与实例状态相关联,则无论如何您都可以将其绑定到实例。在这种情况下,使用您的 API 的人的向后兼容性问题可能会导致难以(或不可能)将该静态方法转换为实例方法。

当然可以!假设您有 interface IMyCollection。它有一个方法 boolean isMutable().

现在您有两个 类、class MyMutableListclass MyImmutableList,它们都实现了 IMyCollection。他们每个人都会覆盖实例方法 isMutable()MyMutableList 只返回 trueMyImmutableList 返回 false.

isMutable() 在 类 中都是一个实例方法,(1)不使用实例变量,(2)不影响实例状态。但是,由于语言的限制(无法重写静态方法),这种设计是唯一实用的。

此外,我想澄清一个误解(正如@manouti 所做的那样):非静态方法不是实例,因为它们使用任何实例变量或影响实例状态;它们是实例方法,因为它们是以这种方式定义的(没有 static 关键字),因此具有隐式 this 参数(在 Python 这样的语言中,它实际上是显式的!) .

由于无法覆盖静态方法,许多关注其代码可测试性的开发人员试图完全避免 Java 中的静态方法。

如果可以用模拟对象替换依赖项,代码将更易于测试。 Mockito 和 EasyMock 是最常用的工具来帮助解决这个问题,它们依靠继承来创建子classes,让你可以轻松地覆盖你不会[的(通常很复杂的)方法 想要测试...以便您的测试专注于您所做 想要测试的内容。

我不会尝试零静态方法的极端,但当我承认包含它们时,出于测试原因,我经常会后悔。

所有这些都非常令人沮丧,因为它与静态方法与实例方法的设计考虑无关。这让我希望那些允许你拥有与 class ...

无关的功能的语言

如果有人正在编写一个人类可读的描述方法的目的,它会提到一个对象吗?如果是这样,请使用实例方法。如果没有,请使用静态方法。请注意,某些方法可能会以任何一种方式描述,在这种情况下,应判断哪种含义更好。

考虑一下,例如,"Get the address to which a Freedonian income tax form should be mailed" 与 "Get the address to which Freedonian income tax forms should be mailed"?第一个问题应该用实例方法来回答;第二个是静态方法。 Freedonia 目前可能要求将所有税表发送到同一地址(在这种情况下,前一种方法可能会忽略所有实例字段),但将来可能会为不同地区的人提供不同的办公室(在这种情况下,前一种方法可能会查看纳税人 ID 和 select 基于此的邮寄地址,而后一种方法必须将表格直接发送到可以接受任何人的表格并根据需要重定向的办公室。

一个很好的例子是布尔值的面向对象编码。大多数语言,甚至像 Java 这样的面向对象的语言,都选择布尔值的面向抽象数据类型的编码,但是例如Smalltalk 使用 OO 编码,并且几乎 none 的方法使用任何实例状态。它看起来有点像这样:

import java.util.function.Supplier;
@FunctionalInterface interface Block { void call(); }

interface Bool {
  Bool not();
  Bool and(Bool other);
  Bool or(Bool other);
  <T> T ifThenElse(Supplier<T> thenBranch, Supplier<T> elseBranch);
  void ifThenElse(Block thenBranch, Block elseBranch);

  static final Bool T = new TrueClass();
  static final Bool F = new FalseClass();

  class TrueClass implements Bool {
    public Bool not() { return F; }
    public Bool and(Bool other) { return other; }
    public Bool or(Bool other) { return this; }
    public <T> T ifThenElse(Supplier<T> thenBranch, Supplier<T> elseBranch) {
      return thenBranch.get();
    }
    public void ifThenElse(Block thenBranch, Block elseBranch) {
      thenBranch.call();
    }
  }

  class FalseClass implements Bool {
    public Bool not() { return T; }
    public Bool and(Bool other) { return this; }
    public Bool or(Bool other) { return other; }
    public <T> T ifThenElse(Supplier<T> thenBranch, Supplier<T> elseBranch) {
      return elseBranch.get();
    }
    public void ifThenElse(Block thenBranch, Block elseBranch) {
      elseBranch.call();
    }
  }
}

public class Main {
  public static void main(String... args) {
    Bool.F.ifThenElse(() -> System.out.println("True"), () -> System.out.println("False"));
    // False
  }
}

事实上,如果您严格遵循 OO,使用大量引用透明的方法,并且支持多态性而不是条件,您通常会在很多子 类 中得到方法,其中每个在 类 returns 中的一个常量值中实现。

我认为有时候是的,因为非静态方法可以重写为不同的class做不同的任务,但任务可能不涉及实例变量,例如:

Fruit.java

public class Fruit{
    public void printInfo(){
        System.out.println("This is fruit");
    }
}

Orange.java

public class Orange extends Fruit{
    public void printInfo(){
        System.out.println("This is orange");
    }
}

Grape.java

public class Grape extends Fruit{
    public void printInfo(){
        System.out.println("This is grape");
    }
}

对象的打印信息:

Fruit f=new Grape();
f.printInfo();