很多“纯面向对象厨”还吐槽像Java、C#的静态方法不面向对象呢。“真正的面向对象”语言里所有操作都应该有receiver,静态方法没有receiver所以不面向对象。
——你们想怎么定义哪种模型更面向对象都罢,俺只管用,怎么样好用都行…
回到题主的问题,每个问题都可以换个角度来争辩:<- 再次声明,俺对这些争辩不感冒,既不支持题主的论点也不是说下面争辩的观点就对。只要是规定得规整的设计俺都OK。
1. 类没有静态属性;
也可以说有静态属性更不面向对象。
类自身是对象,类对象可以有字段,这就很足够了,非常面向对象。
2. 类的静态方法,实例无法调用;
在“真正的面向对象”语言里,类也是一个对象,所谓“静态方法”不过就是挂在类对象上的方法而已。要调用它们就指定该类为receiver去调用即可。ES6的class正好就可以做到这点,很面向对象。
像Java、C#的静态方法没有receiver,是一种比较不面向对象的设计。要从面向对象的角度看,它们可以看作是依赖静态类型系统来去省略了作为receiver的类参数的折衷设计,而它们其实也是要通过“ClassName.methodName(...)”的方式来调用的,只不过Java和C#在名字查找规则上对静态方法做了特殊处理,允许在实例方法里省略类名去调用本类的静态方法。在像ES6这样动态的语言里,这种隐式指定receiver的做法反而不现实也不直观。
Java的语法里还有一种非常恶心的调用静态方法的办法——通过一个实例的引用去调用:
public class Foo { static void bar() { } void baz() { this.bar(); //=> actually compiles into Foo.bar() } public static void main(String[] args) { Foo obj = null; obj.bar(); //=> actually compiles into Foo.bar(), no NPE thrown } }
这里 Foo.bar() 是一个静态方法,但我们可以用调用实例方法的语法(this.bar()、obj.bar())去调用它——而实际的语义跟那个引用一点关系都没有,纯粹是一个静态方法调用而已。这就是为什么当引用是null的时候用这种语法调用静态方法还是可以正常编译并运行,而不会在运行时抛出NullPointerException。这个设计非常糟糕,很容易就把读代码的人骗了,以为这里调用的是实例方法。这绝对不是什么好的面向对象设计。
3. 只能在方法内定义属性;
这跟是否面向对象其实关系不大,而是跟语言的动态性更有关系。
看看同样也很动态的Python和Ruby,它们也都是只能在实例方法里定义实例字段。像Ruby的attr_accessor只是声明了某个名字的getter / setter(合起来叫做accessor),但却没有定义实例字段自身。这只不过是一种动态的设计风格,谈不上面向对象与否。