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

Posted

技术标签:

【中文标题】有一个不使用实例变量的非静态方法有意义吗?【英文标题】:Does it make sense to have a non static method which does not use an instance variable? 【发布时间】:2015-10-27 03:36:36 【问题描述】:

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

但是如果我们有一个不影响或不受实例状态影响的行为,那么这种方法不应该被标记为静态的。

【问题讨论】:

【参考方案1】:

我理解它这样做是因为非静态方法通常最终使用实例变量。

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

换句话说,在以下方法中:

public void foo() 


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

编辑:

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

【讨论】:

我明白。但是我们在实际应用中是否会编写这样的实例方法(即独立于实例变量) @Twister 这是一个不同的问题。在这里,您正在考虑程序员对方法是否应该是静态的做出的逻辑决策。编译器不关心这个:它必须强制执行规则。 这个答案问题如何? @Twister 假设您需要通过一些静态 (硬编码) 规则过滤一些输入参数。并且必须在类的多个方法中应用相同的过滤器。那是现实生活中的static 候选人。【参考方案2】:

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

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

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

【讨论】:

API 参数是这里最重要的一点。 static 方法不能实现继承接口的方法。 static 方法不受多态性的影响。 static 方法的功能实际上非常有限。 @BoristheSpider 当然,这既是福也是祸。许多函数式语言在很大程度上建立在“静态”函数之上——基本上,任何不需要明确地绑定到实例(例如多态接口实现)的东西都倾向于是静态的。在某些方面,它回到了老式的 C 时代,但它非常令人耳目一新。由于函数式编程往往更倾向于组合而不是继承,所以这很有意义。【参考方案3】:

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

现在你有两个类,class MyMutableListclass MyImmutableList,它们都实现了IMyCollection。它们中的每一个都将覆盖实例方法isMutable()MyMutableList 只返回trueMyImmutableList 返回false

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

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

【讨论】:

【参考方案4】:

由于静态方法不能被覆盖,许多担心代码可测试性的开发人员试图完全避免使用 Java 中的静态方法。

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

我并没有走极端去尝试零静态方法,但是当我承认包含它们时,我经常因为测试原因而后悔。

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

【讨论】:

【参考方案5】:

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

例如,请考虑“获取应邮寄弗里多尼亚所得税表的地址”与“获取应邮寄弗里多尼亚所得税表的地址”?第一个问题应该用实例方法来回答;第二个是静态方法。可能 Freedonia 目前要求将所有税表发送到同一个地址(在这种情况下,前一种方法可能会忽略所有实例字段),但将来可能会为不同地区的人们提供不同的办公室(在这种情况下,前一种方法可能会查看纳税人 ID 并据此选择邮寄地址,而后一种方法必须将表格直接发送到可以接受任何人的表格并根据需要重定向它们的办公室。

【讨论】:

【参考方案6】:

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

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,使用大量引用透明的方法,并支持多态而不是条件,你通常会在许多子类中使用方法,其中一个类中的每个实现都会返回一个常数值。

【讨论】:

【参考方案7】:

我认为有时是可以的,因为非静态方法可以覆盖为不同的类执行不同的任务,但任务可能不涉及实例变量,例如:

水果.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");
    

葡萄.java

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

对象的打印信息:

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

【讨论】:

以上是关于有一个不使用实例变量的非静态方法有意义吗?的主要内容,如果未能解决你的问题,请参考以下文章

MFC中静态成员函数调用其他类的非静态变量

JAVA 静态方法不能访问直接

类加载器实例化时的顺序

类加载器实例化时的顺序

varargs 方法的非 varargs 调用

静态变量