Why can't static methods be abstract in Java
问题是在Java中为什么我不能定义抽象的静态方法?例如
1 2 3 4 | abstract class foo { abstract void bar( ); // <-- this is ok abstract static void bar2(); //<-- this isn't why? } |
因为"abstract"的意思是"不实现任何功能","static"的意思是"即使没有对象实例也有功能"。这是一个逻辑上的矛盾。
语言设计不好。直接调用静态抽象方法比仅为使用该抽象方法创建实例更有效。尤其是当使用抽象类作为解决枚举无法扩展的方法时,这是另一个糟糕的设计示例。希望他们在下一个版本中解决这些限制。
不能重写静态方法,因此将其抽象化将毫无意义。此外,抽象类中的静态方法将属于该类,而不是重写类,因此无论如何都不能使用。
方法的
在Java中,EDCOX1×1成员(方法或字段)不能被子类重写(这在其他面向对象语言中不一定是正确的,参见SimalTalk)。EDOCX1×1成员可能被隐藏,但与重写相比根本不同。
由于静态成员不能在子类中被重写,因此不能对其应用
作为旁白,其他语言确实支持静态继承,就像实例继承一样。从语法的角度来看,这些语言通常要求在语句中包含类名。例如,在Java中,假设您在ClassA中编写代码,则这些语句是等效语句(如果方法A()是静态方法,并且没有具有相同签名的实例方法):
1 | ClassA.methodA(); |
和
1 | methodA(); |
在smalltalk中,类名不是可选的,因此语法是(注意smalltalk不使用)。将"主语"和"动词"分开,但将其用作Statemend终结符):
1 | ClassA methodA. |
因为类名称总是必需的,所以始终可以通过遍历类层次结构来确定方法的正确"版本"。因为它的价值,我偶尔会错过EDCOX1的1继承,并且在我第一次使用Java时被Java中缺少静态继承所咬。此外,smalltalk是duck类型的(因此不支持契约式的程序),因此它没有类成员的
我也问了同样的问题,这就是为什么
因为抽象类会说,它不会给出实现并允许子类给出它
所以子类必须重写超类的方法,
规则1-不能重写静态方法
因为静态成员和方法是编译时元素,所以允许静态方法的重载(编译时多态性),而不是重写(运行时多态性)。
所以,它们不能是抽象的。
在Java宇宙中没有任何抽象抽象的东西。
这是一个糟糕的语言设计,真的没有理由解释为什么它是不可能的。
事实上,这里有一个关于如何在Java中实现的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | public class Main { public static void main(String[] args) { // This is done once in your application, usually at startup Request.setRequest(new RequestImplementationOther()); Request.doSomething(); } public static final class RequestImplementationDefault extends Request { @Override void doSomethingImpl() { System.out.println("I am doing something AAAAAA"); } } public static final class RequestImplementaionOther extends Request { @Override void doSomethingImpl() { System.out.println("I am doing something BBBBBB"); } } // Static methods in here can be overriden public static abstract class Request { abstract void doSomethingImpl(); // Static method public static void doSomething() { getRequest().doSomethingImpl(); } private static Request request; private static Request getRequest() { // If setRequest is never called prior, it will default to a default implementation. Of course you could ignore that too. if ( request == null ) { return request = new RequestImplementationDefault(); } return request; } public static Request setRequest(Request r){ return request = r; } } } |
以下是旧的例子==
查找getrequest和getrequestimpl…可以在调用之前调用setInstance来更改实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; /** * @author Mo. Joseph * @date 16 mar 2012 **/ public abstract class Core { // --------------------------------------------------------------- private static Core singleton; private static Core getInstance() { if ( singleton == null ) setInstance( new Core.CoreDefaultImpl() ); // See bottom for CoreDefaultImpl return singleton; } public static void setInstance(Core core) { Core.singleton = core; } // --------------------------------------------------------------- // Static public method public static HttpServletRequest getRequest() { return getInstance().getRequestImpl(); } // A new implementation would override this one and call setInstance above with that implementation instance protected abstract HttpServletRequest getRequestImpl(); // ============================ CLASSES ================================= // ====================================================================== // == Two example implementations, to alter getRequest() call behaviour // == getInstance() have to be called in all static methods for this to work // == static method getRequest is altered through implementation of getRequestImpl // ====================================================================== /** Static inner class CoreDefaultImpl */ public static class CoreDefaultImpl extends Core { protected HttpServletRequest getRequestImpl() { return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } } /** Static inner class CoreTestImpl : Alternative implementation */ public static class CoreTestImpl extends Core { protected HttpServletRequest getRequestImpl() { return new MockedRequest(); } } } |
抽象方法的定义只能使其在子类中被重写。但是,不能重写静态方法。因此,拥有一个抽象的静态方法是一个编译时错误。
现在,下一个问题是为什么静态方法不能被重写??
这是因为静态方法属于一个特定的类,而不是它的实例。如果您试图重写一个静态方法,您将不会得到任何编译或运行时错误,但编译器只会隐藏超类的静态方法。
我看到上帝已经有无数的答案,但我看不出任何实际的解决办法。当然,这是一个真正的问题,在Java中没有排除这种语法的充分理由。由于最初的问题缺少可能需要的上下文,因此我提供了上下文和解决方案:
假设在一组相同的类中有一个静态方法。这些方法调用特定于类的静态方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class C1 { static void doWork() { ... for (int k: list) doMoreWork(k); ... } private static void doMoreWork(int k) { // code specific to class C1 } } class C2 { static void doWork() { ... for (int k: list) doMoreWork(k); ... } private static void doMoreWork(int k) { // code specific to class C2 } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | abstract class C { static void doWork() { ... for (int k: list) doMoreWork(k); ... } static abstract void doMoreWork(int k); } class C1 extends C { private static void doMoreWork(int k) { // code for class C1 } } class C2 extends C { private static void doMoreWork(int k) { // code for class C2 } } |
但这不会编译,因为不允许使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | abstract class C { void doWork() { ... for (int k: list) doMoreWork(k); ... } abstract void doMoreWork(int k); } class C1 { private static final C c = new C(){ @Override void doMoreWork(int k) { System.out.println("code for C1"); } }; public static void doWork() { c.doWork(); } } class C2 { private static final C c = new C() { @Override void doMoreWork(int k) { System.out.println("code for C2"); } }; public static void doWork() { c.doWork(); } } |
使用此解决方案,唯一重复的代码是
1 2 3 | public static void doWork() { c.doWork(); } |
假设有两类,
1 2 3 4 5 6 7 | abstract class Parent { abstract void run(); } class Child extends Parent { void run() {} } |
这意味着任何
但是,现在假设
1 2 3 | class Parent { static void run() {} } |
这意味着
另外,使用
根据定义,静态方法不需要知道
根据这一点,抽象静态方法将是非常无用的,因为您永远不会让它的引用被某个已定义的主体替换。
抽象类不能有静态方法,因为抽象是为了实现动态绑定而进行的,而静态方法是静态绑定到它们的功能上的。行为不依赖实例变量,因此没有实例/对象是必需的。只有类。静态方法属于类,而不是对象。它们存储在一个称为permgen的内存区域中,在该区域中它与每个对象共享。抽象类中的方法动态绑定到其功能。
可以在没有类实例的情况下调用静态方法。在您的示例中,您可以调用foo.bar2(),但不能调用foo.bar(),因为对于bar,您需要一个实例。以下代码可以工作:
1 2 | foo var = new ImplementsFoo(); var.bar(); |
如果调用静态方法,它将始终以相同的代码执行。在上面的示例中,即使您在implementsfoo中重新定义了bar2,对var.bar2()的调用也会执行foo.bar2()。
如果bar2现在没有实现(这就是抽象的意思),您可以调用一个没有实现的方法。那是非常有害的。
我相信我已经找到了这个问题的答案,其形式是为什么接口的方法(类似于父类中的抽象方法)不能是静态的。这是完整的答案(不是我的)
基本上静态方法可以在编译时绑定,因为要调用它们,需要指定一个类。这与实例方法不同,因为在编译时调用该方法的引用的类可能是未知的(因此只能在运行时确定调用哪个代码块)。
如果您正在调用一个静态方法,那么您已经知道它的实现所在的类,或者它的任何直接子类。如果你定义
1 2 3 4 5 6 7 8 | abstract class Foo { abstract static void bar(); } class Foo2 { @Override static void bar() {} } |
那么任何
考虑到这一点,静态抽象方法的唯一目的是强制子类来实现这种方法。最初您可能认为这是非常错误的,但是如果您有一个通用类型参数
那么,它会有用吗?是的,也许这就是为什么Java 8允许接口中的静态方法(虽然只有默认实现)。为什么不在类中使用默认实现抽象静态方法?因为一个带有默认实现的抽象方法实际上是一个具体的方法。
为什么不使用没有默认实现的抽象/接口静态方法?显然,仅仅是因为Java识别它必须执行哪个代码块(我的答案的第一部分)。
因为抽象类是一个oops概念,静态成员不是oops的一部分…现在的问题是,我们可以在接口中声明静态的完整方法,并且可以通过在接口中声明主方法来执行接口。
1 2 3 4 5 6 |
拥有一个抽象静态方法的想法是,您不能直接为该方法使用特定的抽象类,但只允许第一个派生实现该静态方法(或者对于泛型:您使用的泛型的实际类)。
这样,您就可以创建例如SortableObject抽象类甚至接口使用(auto-)抽象静态方法,该方法定义排序选项的参数:
1 2 3 4 |
现在,您可以定义一个可排序对象,该对象可以按所有这些对象相同的主要类型排序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public class MyDataObject implements SortableObject { final static String [] SORT_TYPES = { "Name","Date of Birth" } static long newDataIndex = 0L ; String fullName ; String sortableDate ; long dataIndex = -1L ; public MyDataObject(String name, int year, int month, int day) { if(name == null || name.length() == 0) throw new IllegalArgumentException("Null/empty name not allowed."); if(!validateDate(year,month,day)) throw new IllegalArgumentException("Date parameters do not compose a legal date."); this.fullName = name ; this.sortableDate = MyUtils.createSortableDate(year,month,day); this.dataIndex = MyDataObject.newDataIndex++ ; } public String toString() { return""+this.dataIndex+"."this.fullName+" ("+this.sortableDate+")"; } // override SortableObject public static String [] getSortableTypes() { return SORT_TYPES ; } public String getSortableValueByType(String type) { int index = MyUtils.getStringArrayIndex(SORT_TYPES, type); switch(index) { case 0: return this.name ; case 1: return this.sortableDate ; } return toString(); // in the order they were created when compared } } |
现在您可以创建一个
1 | public class SortableList<T extends SortableObject> |
它可以检索类型,构建一个弹出菜单来选择要排序的类型,并通过从该类型获取数据来使用列表,以及一个添加函数,当选择了排序类型时,该函数可以自动对新项目排序。注意SortableList实例可以直接访问静态方法"t":
1 |
必须使用实例的问题是SortableList可能还没有项,但已经需要提供首选排序。
再见,奥拉夫。
首先,抽象类的一个关键点-无法实例化抽象类(请参见wiki)。因此,不能创建抽象类的任何实例。
现在,Java处理静态方法的方式是通过与该类的所有实例共享该方法。
因此,如果您不能实例化一个类,那么该类就不能有抽象静态方法,因为抽象方法要求扩展。
繁荣。
根据Java文档:
A static method is a method that is associated with the class in which
it is defined rather than with any object. Every instance of the class
shares its static methods
在Java 8中,与默认方法一起,在接口中也允许使用静态方法。这使我们更容易在库中组织助手方法。我们可以将特定于接口的静态方法保存在同一个接口中,而不是单独的类中。
一个很好的例子是:
1 | list.sort(ordering); |
而不是
1 |
另一个使用静态方法的例子也在文档本身中给出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public interface TimeClient { // ... static public ZoneId getZoneId (String zoneString) { try { return ZoneId.of(zoneString); } catch (DateTimeException e) { System.err.println("Invalid time zone:" + zoneString + "; using default time zone instead."); return ZoneId.systemDefault(); } } default public ZonedDateTime getZonedDateTime(String zoneString) { return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString)); } } |
因为"abstract"意味着要重写方法,而不能重写"static"方法。
当常规方法被子类覆盖并提供功能时,它们可以是抽象的。假设类
现在,根据定义静态方法属于类,它们与类的对象或其子类的对象无关。它们甚至不需要它们存在,不需要实例化类就可以使用它们。因此,它们需要准备好,并且不能依赖子类向它们添加功能。
因为abstract是应用于abstract方法的关键字,所以不指定主体。如果我们谈论静态关键字,它属于类区域。
因为如果在类中使用任何静态成员或静态变量,它将在类加载时加载。
声明一个方法为
可以用Java 8中的接口实现这一点。
这是关于它的官方文件:
https://docs.oracle.com/javase/tutorial/java/iandi/defaultmethods.html网站
因为如果一个类扩展了一个抽象类,那么它必须重写抽象方法,这是必需的。由于静态方法是在编译时解析的类方法,而重写的方法是在运行时解析的实例方法,并遵循动态多态性。