【发布时间】:2015-10-27 03:36:36
【问题描述】:
编译器不允许静态方法调用非静态方法。我理解它这样做是因为非静态方法通常最终使用实例变量。
但是有一个不使用实例变量的非静态方法有意义吗?如果我们有一个不影响或不受实例状态影响的行为,那么这种方法不应该被标记为静态的。
【问题讨论】:
标签: java methods static instance non-static
编译器不允许静态方法调用非静态方法。我理解它这样做是因为非静态方法通常最终使用实例变量。
但是有一个不使用实例变量的非静态方法有意义吗?如果我们有一个不影响或不受实例状态影响的行为,那么这种方法不应该被标记为静态的。
【问题讨论】:
标签: java methods static instance non-static
我理解它这样做是因为非静态方法通常最终使用实例变量。
即使实例方法不使用实例变量,它仍然绑定到类的实例。实际上,它在方法参数中隐式引用了this。
换句话说,在以下方法中:
public void foo() {
}
this 作为方法中的第一个局部变量隐式传递。
编辑:
重新阅读这个问题,这是一个更广泛的问题,取决于具体情况。通常,如果该方法不需要实例(并且您很确定它不需要),那么只需将其设为 static。
【讨论】:
static 候选人。
很多时候,不。如果该方法不涉及任何实例状态,则没有理由将其绑定到实例。
当然,静态方法不能被继承或覆盖,所以很明显你会想要一个不使用实例状态的实例方法。 strategy pattern 就是一个典型的例子。
另一种可能将其绑定到实例的情况是,如果这是一个公共 API,并且您认为将来可能希望将该方法绑定到实例状态。在这种情况下,对于使用您的 API 的人来说,向后兼容性问题可能会导致难以(或不可能)将该静态方法转换为实例方法。
【讨论】:
static 方法不能实现继承接口的方法。 static 方法不受多态性的影响。 static 方法的功能实际上非常有限。
当然!假设您有interface IMyCollection。它有一个方法boolean isMutable()。
现在你有两个类,class MyMutableList 和class MyImmutableList,它们都实现了IMyCollection。它们中的每一个都将覆盖实例方法isMutable(),MyMutableList 只返回true,MyImmutableList 返回false。
isMutable() 在两个类中都是一个实例方法,(1)不使用实例变量,(2)不影响实例状态。但是,由于语言的限制(不可能覆盖静态方法),这种设计是唯一实用的设计。
另外,我想澄清一个误解(正如@manouti 所做的那样):非静态方法不是实例,因为它们使用任何实例变量或影响实例状态;它们是实例方法,因为它们是以这种方式定义的(没有 static 关键字),因此具有隐式的 this 参数(在 Python 等语言中,它实际上是显式的!)。
【讨论】:
由于静态方法不能被覆盖,许多担心代码可测试性的开发人员试图完全避免使用 Java 中的静态方法。
如果可以用模拟对象替换依赖项,则代码更具可测试性。 Mockito 和 EasyMock 是帮助解决此问题的最常用工具,它们依靠继承来创建子类,让您可以轻松地覆盖您不想想要测试的(通常很复杂的)方法。 . 以便您的测试专注于您要做想要测试的内容。
我并没有走极端去尝试零静态方法,但是当我承认包含它们时,我经常因为测试原因而后悔。
所有这些都非常令人沮丧,因为它与静态与实例方法的设计考虑无关。这让我希望那些允许你拥有与类无关的功能的语言......
【讨论】:
如果要写一个人类可读的描述方法的目的,它会提到一个对象吗?如果是这样,请使用实例方法。如果没有,请使用静态方法。请注意,某些方法可能会以任何一种方式描述,在这种情况下,应该判断哪种含义更好。
例如,请考虑“获取应邮寄弗里多尼亚所得税表的地址”与“获取应邮寄弗里多尼亚所得税表的地址”?第一个问题应该用实例方法来回答;第二个是静态方法。可能 Freedonia 目前要求将所有税表发送到同一个地址(在这种情况下,前一种方法可能会忽略所有实例字段),但将来可能会为不同地区的人们提供不同的办公室(在这种情况下,前一种方法可能会查看纳税人 ID 并据此选择邮寄地址,而后一种方法必须将表格直接发送到可以接受任何人的表格并根据需要重定向它们的办公室。
【讨论】:
一个很好的例子是布尔值的面向对象编码。大多数语言,甚至像 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,使用大量引用透明的方法,并支持多态而不是条件,你通常会在许多子类中使用方法,其中一个类中的每个实现都会返回一个常数值。
【讨论】:
我认为有时是可以的,因为非静态方法可以覆盖为不同的类执行不同的任务,但任务可能不涉及实例变量,例如:
水果.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();
【讨论】: