为什么我不能在Java接口中定义静态方法呢?

Why can't I define a static method in a Java interface?

示例如下:

1
2
3
4
5
public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

当然不行。但是为什么不呢?

其中一个可能的问题是,当你打电话给:

1
IXMLizable.newInstanceFromXML(e);

在这种情况下,我认为它应该只调用一个空方法(即)。所有子类都将被强制实现静态方法,因此在调用静态方法时它们都可以。为什么不可能呢?

编辑:我想我正在寻找比"因为这就是Java的方式"更深的答案。

静态方法不能被覆盖有什么特殊的技术原因吗?也就是说,为什么Java设计者决定使实例方法可以克服,而不是静态方法呢?

编辑:我的设计的问题是我正在尝试使用接口来执行编码约定。

也就是说,接口的目标是双重的:

  • 我希望Ixmlizable接口允许我将实现它的类转换为XML元素(使用多态性,可以很好地工作)。

  • 如果有人想要创建一个实现Ixmlizable接口的类的新实例,他们总是知道将有一个newInstanceFromXML(element e)静态构造函数。

  • 除了在接口中添加注释,还有其他方法来确保这一点吗?

    编辑:对于Java 8,在接口中允许使用静态方法。


    Java 8允许静态接口方法

    使用Java 8,接口可以具有静态方法。它们也可以有具体的实例方法,但不能有实例字段。

    这里有两个问题:

  • 为什么在糟糕的过去,接口不能包含静态方法?
  • 为什么不能重写静态方法?
  • 接口中的静态方法

    在以前的版本中,接口不可能有静态方法,这并没有很强的技术原因。这个问题的海报很好地概括了这一点。静态接口方法最初被认为是一个小的语言变化,然后有一个官方的建议将它们添加到Java 7中,但是后来由于意外的并发症而下降。

    最后,Java 8引入了静态接口方法,以及用默认实现重写可用实例方法。但它们仍然不能有实例字段。这些特性是lambda表达式支持的一部分,您可以在JSR 335的第H部分中了解它们。

    重写静态方法

    第二个问题的答案有点复杂。

    静态方法在编译时是可解析的。动态调度对实例方法很有意义,因为编译器无法确定对象的具体类型,因此无法解析要调用的方法。但是调用静态方法需要一个类,因为该类在编译时是静态的,所以不需要动态调度。

    了解实例方法是如何工作的,有必要了解这里的情况。我确信实际的实现是完全不同的,但是让我解释一下方法调度的概念,它精确地模拟观察到的行为。

    假设每个类都有一个哈希表,它将方法签名(名称和参数类型)映射到实际的代码块以实现该方法。当虚拟机尝试在实例上调用方法时,它查询对象的类,并在类的表中查找请求的签名。如果找到方法体,则调用它。否则,将获取该类的父类,并在此重复查找。这将一直持续到找到方法为止,或者没有更多的父类&mdash;,从而导致NoSuchMethodError

    如果超类和子类的表中都有相同方法签名的条目,则首先会遇到子类的版本,并且永远不会使用超类的版本&mdash;这是一个"重写"。

    现在,假设我们跳过对象实例,从一个子类开始。解决方法可以如上所述进行,为您提供一种"可重写"的静态方法。然而,由于编译器是从一个已知的类开始的,而不是等到运行时为其类查询一个未指定类型的对象,所以解决方案都可以在编译时发生。在"重写"静态方法中没有意义,因为可以始终指定包含所需版本的类。

    建造商"接口"

    这里有更多的材料来解决这个问题最近的编辑。

    听起来,您希望为IXMLizable的每个实现有效地委托一个类似于构造函数的方法。暂时不要用接口来强制执行这个,并假设有一些类满足这个要求。你将如何使用它?

    1
    2
    3
    4
    5
    class Foo implements IXMLizable<Foo> {
      public static Foo newInstanceFromXML(Element e) { ... }
    }

    Foo obj = Foo.newInstanceFromXML(e);

    由于在"构造"新对象时必须显式地命名具体类型Foo,因此编译器可以验证它确实具有必要的工厂方法。如果没有,那又怎样?如果我可以实现一个缺少"构造函数"的IXMLizable,并且我创建一个实例并将其传递给您的代码,那么它就是一个IXMLizable,具有所有必要的接口。

    构造是实现的一部分,而不是接口。任何成功使用接口的代码都不关心构造函数。任何关心构造函数的代码无论如何都需要知道具体的类型,并且可以忽略接口。


    这里已经有人问过了

    复制我的答案:

    在接口中声明静态方法从来没有意义。它们不能由普通调用MyInterface.StaticMethod()执行。如果您通过指定实现类myImplementor.staticMethod()来调用它们,那么您必须知道实际的类,因此无论接口是否包含它都是不相关的。

    更重要的是,静态方法永远不会被重写,如果您尝试这样做:

    1
    2
    MyInterface var = new MyImplementingClass();
    var.staticMethod();

    静态规则表示必须执行在声明的var类型中定义的方法。因为这是一个接口,所以这是不可能的。

    无法执行"result=myInterface.staticMethod()"的原因是它必须执行在myInterface中定义的方法版本。但MyInterface中不能定义版本,因为它是一个接口。它没有定义的代码。

    虽然你可以说这相当于"因为Java是这样做的",实际上,决策是其他设计决策的逻辑结果,也是出于非常好的理由。


    通常这是使用工厂模式完成的。

    1
    2
    3
    4
    5
    6
    7
    public interface IXMLizableFactory<T extends IXMLizable> {
      public T newInstanceFromXML(Element e);
    }

    public interface IXMLizable {
      public Element toXMLElement();
    }


    随着Java 8的出现,现在可以在接口中写入默认和静态方法。docs.oracle/staticmethod文件

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    public interface Arithmetic {

        public int add(int a, int b);

        public static int multiply(int a, int b) {
            return a * b;
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class ArithmaticImplementation implements Arithmetic {

        @Override
        public int add(int a, int b) {
            return a + b;
        }

        public static void main(String[] args) {
            int result = Arithmetic.multiply(2, 3);
            System.out.println(result);
        }
    }

    结果:6

    提示:调用静态接口方法不需要由任何类实现。当然,这是因为超类中静态方法的规则同样适用于接口上的静态方法。


    因为静态方法不能在子类中被重写,因此它们不能是抽象的。实际上,接口中的所有方法都是抽象的。


    Why can't I define a static method in a Java interface?

    实际上,你可以在Java 8中使用。

    根据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
    Collections.sort(list, ordering);

    静态方法示例(来自文档本身):

    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));
        }    
    }

    第一,所有的决定是由语言决定的语言的创造者。有没有什么在世界软件工程定义的编译器/解释器或语言或写作是一个静态方法不能说在接口的一部分。我已经写了几个大学语言和编译器他们——这一切都只是坐下来和有意义的语义定义。我认为,这一方法在静态语义的接口是明确的-即使remarkably编译器已决议引用他们的方法的运行时间。

    其次,我们使用的是静态的方法,在所有的,有一个有效的理由的均值有接口模式包括一个静态方法不能说任何你的,但我使用静态方法的基础上定期。

    最可能的答案是,没有正确的感知的需求,在什么时间语言定义的静态方法的接口。Java有很多多年来发展项目,这是安切洛蒂显然得到一些利益。这表明,看着for Java 7它是地狱的一个层次的兴趣可能导致语言的变化。我一想,快乐的时候,我不再有instantiate对象我这样我可以调用非静态方法访问静态变量的吸气剂在A或A收藏指正.


    接口与多态性有关,多态性本质上与对象实例相关,而不是与类相关。因此,静态在接口上下文中没有意义。


    静态方法不是虚拟的,类似于实例方法,所以我猜想Java设计者决定他们不希望它们在接口中。

    但您可以将包含静态方法的类放入接口中。你可以试试看!

    1
    2
    3
    4
    5
    6
    7
    public interface Test {
        static class Inner {
            public static Object get() {
                return 0;
            }
        }
    }


    • "静态方法不能被重写的特殊原因是什么?"

    让我通过填写定义为您重新回答这个问题。

    • "是否有特定原因导致在编译时解析的方法无法在运行时解析。"

    或者,更完整地说,如果我想调用一个不带实例的方法,但是知道类,我如何根据我没有的实例来解决它。


    评论EDIT: As of Java 8, static methods are now allowed in interfaces.

    这是正确的,因为Java 8允许在接口中使用静态方法,但是您的示例仍然无法工作。不能只定义静态方法:必须实现它,否则将获得编译错误。


    1
    Why can't I define a static method in a Java interface?

    接口中的所有方法都是显式抽象的,因此不能将它们定义为静态的,因为静态方法不能是抽象的。


    几个答案讨论了可重写静态方法概念的问题。然而,有时你会遇到一种模式,它似乎正是你想要使用的。

    例如,我使用的对象关系层有值对象,但也有操作值对象的命令。由于各种原因,每个值对象类都必须定义一些静态方法,以便框架找到命令实例。例如,要创建一个人,您需要:

    1
    2
    cmd = createCmd(Person.getCreateCmdId());
    Person p = cmd.execute();

    用身份证载人

    1
    2
    3
    cmd = createCmd(Person.getGetCmdId());
    cmd.set(ID, id);
    Person p = cmd.execute();

    这是相当方便的,但是它有它的问题;特别是静态方法的存在不能在接口中强制执行。接口中可重写的静态方法正是我们所需要的,只要它能以某种方式工作。

    EJB通过有一个家庭接口来解决这个问题;每个对象都知道如何找到自己的家庭,家庭包含"静态"方法。通过这种方式,"静态"方法可以根据需要被重写,并且您不会将普通(它称为"远程")接口与不应用于be an实例的方法混淆。只需让普通接口指定一个"gethome()"方法。返回home对象的一个实例(我想可能是单例的),调用方可以执行影响所有person对象的操作。


    好吧,没有泛型,静态接口是无用的,因为所有静态方法调用都是在编译时解析的。所以,它们没有真正的用途。

    对于泛型,它们可以使用——有或没有默认实现。很明显,需要被覆盖等等。然而,我的猜测是,这样的使用并不十分OO(正如其他答案所指出的那样),因此被认为不值得他们为有效地实施所需的努力。


    永远不会dereferenced statically CAN接口,例如ISomething.member。接口总是通过一个dereferenced到实例变量是一个接口收藏指正.因此,在接口参考永远不会知道它是一个没有实例收藏指正其收藏指正.

    因此,一个接近的近似方法在静态界面,非静态方法会是"本",即盘不访问任何非静态成员的实例。在低层次的抽象,每一个非静态方法(后查找在任何位置)真的是只是一个类函数的范围为"本"的任务参数的隐式形式。湖的Singleton对象和Scala和Java作为证据是互操作性的概念。因此,每一个静态方法和类是一个函数的范围是不带参数的"本"。因此,低价格的静态方法可以称之为statically,但以前没有执行,接口是抽象的)。

    因此,得到一个近似的方法closest静态界面,是使用非静态方法,那么不要访问任何非静态实例成员。有没有任何其他可能的性能收益的方式,因为没有办法statically链接(在编译时)a ISomething.member()。唯一的海上利益I of a静态方法,它将不会是在输入接口(即忽略)这样的disallow隐"这"和访问的任何非静态实例成员。这将是一个implicitly函数不能访问"本",是immutate甚至不是只读的背景和其与含氟类。但在宣言的"静态"界面会因此ISomething工业的人谁试图访问它ISomething.member()它会造成编译错误。我想如果我编译错误说明什么,这将是更好的比试图教育人们关于使用非静态方法来完成他们想要的是什么(主要是作为工厂方法),我们在这里做(和已经重复3 Q &;a时代在这个网站),所以它是显然,这是不直观的问题对很多体育的人。我不得不认为它一会儿得到正确的理解。

    的方式,把一mutable接口是使用静态场的非静态的getter和setter方法在静电场的访问接口,这是在收藏指正.sidenote是不可变的statics可以被Java的接口在一个static final


    解决这个问题:错误:缺少方法体或声明抽象静态void main(string[]args);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    interface I
    {
        int x=20;
        void getValue();
        static void main(String[] args){};//Put curly braces
    }
    class InterDemo implements I
    {
        public void getValue()
        {
        System.out.println(x);
        }
        public static void main(String[] args)
        {
        InterDemo i=new InterDemo();
        i.getValue();  
        }

    }

    输出:二十

    现在我们可以在接口中使用静态方法


    可以实现的是静态接口(而不是接口中的静态方法)。实现给定静态接口的所有类都应该实现相应的静态方法。可以从任何类clazz获得静态接口si

    1
    2
    3
    SI si = clazz.getStatic(SI.class); // null if clazz doesn't implement SI
    // alternatively if the class is known at compile time
    SI si = Someclass.static.SI; // either compiler errror or not null

    然后你可以打电话给si.method(params)。这将非常有用(例如对于工厂设计模式),因为您可以从编译时未知类获取(或检查)si静态方法实现!动态调度是必需的,并且可以通过扩展类(通过静态接口调用时)来重写类的静态方法(如果不是最终方法)。显然,这些方法只能访问其类的静态变量。


    虽然我意识到Java 8解决了这个问题,但我想我会插手我目前正在处理的一个场景(锁定使用Java 7),其中能够在接口中指定静态方法是有帮助的。

    我有几个枚举定义,其中我定义了"id"和"displayname"字段,以及基于各种原因评估值的助手方法。实现接口允许我确保getter方法已就位,而不是静态helper方法。作为一个枚举,实际上没有一种干净的方法可以将助手方法卸载到继承的抽象类或类似的类中,因此必须在枚举本身中定义这些方法。另外,因为它是一个枚举,所以您永远无法将它作为实例化对象传递,并将其视为接口类型,但是能够通过接口来支持静态助手方法的存在是我喜欢在Java 8中支持的方法。

    这是说明我观点的代码。

    接口定义:

    1
    2
    3
    4
    5
    public interface IGenericEnum <T extends Enum<T>> {
        String getId();
        String getDisplayName();
        //If I was using Java 8 static helper methods would go here
    }

    一个枚举定义的示例:

    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
    public enum ExecutionModeType implements IGenericEnum<ExecutionModeType> {
        STANDARD ("Standard","Standard Mode"),
        DEBUG ("Debug","Debug Mode");

        String id;
        String displayName;

        //Getter methods
        public String getId() {
            return id;
        }

        public String getDisplayName() {
            return displayName;
        }

        //Constructor
        private ExecutionModeType(String id, String displayName) {
            this.id = id;
            this.displayName = displayName;
        }

        //Helper methods - not enforced by Interface
        public static boolean isValidId(String id) {
            return GenericEnumUtility.isValidId(ExecutionModeType.class, id);
        }

        public static String printIdOptions(String delimiter){
            return GenericEnumUtility.printIdOptions(ExecutionModeType.class, delimiter);
        }

        public static String[] getIdArray(){
            return GenericEnumUtility.getIdArray(ExecutionModeType.class);
        }

        public static ExecutionModeType getById(String id) throws NoSuchObjectException {
            return GenericEnumUtility.getById(ExecutionModeType.class, id);
        }
    }

    通用枚举实用程序定义:

    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
    public class GenericEnumUtility {
        public static <T extends Enum<T> & IGenericEnum<T>> boolean isValidId(Class<T> enumType, String id) {      
            for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
                if(enumOption.getId().equals(id)) {
                    return true;
                }
            }

            return false;
        }

        public static <T extends Enum<T> & IGenericEnum<T>> String printIdOptions(Class<T> enumType, String delimiter){
            String ret ="";
            delimiter = delimiter == null ?"" : delimiter;

            int i = 0;
            for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
                if(i == 0) {
                    ret = enumOption.getId();
                } else {
                    ret += delimiter + enumOption.getId();
                }          
                i++;
            }

            return ret;
        }

        public static <T extends Enum<T> & IGenericEnum<T>> String[] getIdArray(Class<T> enumType){
            List<String> idValues = new ArrayList<String>();

            for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
                idValues.add(enumOption.getId());
            }

            return idValues.toArray(new String[idValues.size()]);
        }

        @SuppressWarnings("unchecked")
        public static <T extends Enum<T> & IGenericEnum<T>> T getById(Class<T> enumType, String id) throws NoSuchObjectException {
            id = id == null ?"" : id;
            for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
                if(id.equals(enumOption.getId())) {
                    return (T)enumOption;
                }
            }

            throw new NoSuchObjectException(String.format("ERROR: "%s" is not a valid ID. Valid IDs are: %s.", id, printIdOptions(enumType," ,")));
        }
    }

    假设在接口中允许静态方法:*它们将强制所有实现类声明该方法。*接口通常是通过对象来使用的,因此对于这些对象,唯一有效的方法就是非静态方法。*任何知道特定接口的类都可以调用其静态方法。因此,将在下面调用实现类的静态方法,但调用程序类不知道是哪个。怎么知道?它没有实例化来猜测!

    界面被认为是在处理对象时使用的。这样,就可以从一个特定的类中实例化一个对象,从而解决最后一个问题。调用类不需要知道哪个特定类,因为实例化可以由第三个类完成。所以调用类只知道接口。

    如果我们希望将此扩展到静态方法,我们应该可以在前面指定一个实现类,然后将引用传递给调用类。这可以通过接口中的静态方法使用类。但是这个引用和一个对象有什么区别呢?我们只需要一个表示类的对象。现在,对象表示旧的类,并可以实现一个新的接口,包括旧的静态方法——这些方法现在是非静态的。

    元类就是为了这个目的。您可以尝试Java类类。但问题是Java不够灵活。不能在接口的类对象中声明方法。

    这是一个元问题-当你需要做屁股

    胡说八道

    不管怎样,您有一个简单的解决方法——使用相同的逻辑使方法非静态。但是,您必须首先创建一个对象来调用该方法。


    不能在接口中定义静态方法,因为静态方法属于类而不是类的实例,并且接口不是类。在这里阅读更多。

    但是,如果需要,可以这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class A {
      public static void methodX() {
      }
    }

    public class B extends A {
      public static void methodX() {
      }
    }

    在本例中,您所拥有的是两个具有两个不同静态方法(称为methodX())的类。


    接口只是提供一个类将提供的东西的列表,而不是这些东西的实际实现,这就是您的静态项。

    如果需要静态,请使用抽象类并继承它,否则请移除静态类。

    希望有帮助!


    假设您可以这样做;考虑这个例子:

    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
    interface Iface {
      public static void thisIsTheMethod();
    }

    class A implements Iface {

      public static void thisIsTheMethod(){
        system.out.print("I'm class A");
      }

    }

    class B extends Class A {

      public static void thisIsTheMethod(){
        System.out.print("I'm class B");
      }
    }

    SomeClass {

      void doStuff(Iface face) {
        IFace.thisIsTheMethod();
        // now what would/could/should happen here.
      }

    }


    我认为Java不具有静态接口方法,因为您不需要它们。你可能认为你有,但是…你将如何使用它们?如果你想这样称呼他们

    1
    MyImplClass.myMethod()

    那么您就不需要在接口中声明它了。如果你想这样称呼他们

    1
    myInstance.myMethod()

    那么它不应该是静态的。如果您实际上打算使用第一种方法,但只想强制每个实现都有这样的静态方法,那么它实际上是一种编码约定,而不是实现接口和调用代码的实例之间的约定。

    接口允许您定义实现接口的类实例和调用代码之间的协定。而Java可以帮助你确保这个合同没有被违反,所以你可以信赖它,不用担心什么样的班级实现了这个合同,只是"签订合同的人"就足够了。对于静态接口,您的代码

    1
    MyImplClass.myMethod()

    不依赖于每个接口实现具有此方法的事实,因此您不需要Java来帮助您确定它。


    什么是需要在接口的静态方法,静态方法使用的基本上是当你没有创建对象实例的整个想法是把对象的接口的概念和方法的介绍,你diverting从静态的概念。