首页 文章

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

提问于
浏览
435

这是一个例子:

public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

当然这不起作用 . 但为什么不呢?

其中一个可能的问题是,当您致电时会发生什么:

IXMLizable.newInstanceFromXML(e);

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

EDIT: 我想'm looking for answer that'比"because that's the way Java is"更深 .

是否有一个特殊的技术原因导致静态方法无法被覆盖?也就是说,为什么Java的设计者决定使实例方法可以覆盖但不是静态方法?

EDIT: 我的设计问题是我正在尝试使用接口来强制执行编码约定 .

也就是说,界面的目标是双重的:

  • 我希望IXMLizable接口允许我将实现它的类转换为XML元素(使用多态,工作正常) .

  • 如果有人想创建一个实现IXMLizable接口的类的新实例,他们将始终知道将有一个newInstanceFromXML(Element e)静态构造函数 .

除了在界面中添加评论之外,还有其他方法可以确保这一点吗?

EDIT: 从Java 8开始,接口中现在允许使用静态方法 .

24 回答

  • 0

    接口只提供一个类将提供的事物列表,而不是这些事物的实际实现,这是您的静态项目 .

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

    希望有所帮助!

  • 43

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

    使用泛型,他们可以使用 - 有或没有默认实现 . 显然需要重写等等 . 但是,我的猜测是这种用法并不是非常OO(正如其他答案明确指出的那样),因此不值得他们有效实施所需的努力 .

  • 9

    静态方法不像实例方法那样虚拟,所以我想Java设计者认为他们不希望它们在接口中 .

    但是您可以将包含静态方法的类放在接口中 . 你可以试试!

    public interface Test {
        static class Inner {
            public static Object get() {
                return 0;
            }
        }
    }
    
  • 1

    接口中静态方法的需要是什么,静态方法基本上是在你不必创建对象实例的时候使用整个接口的想法是引入OOP概念,引入静态方法你是从概念转移 .

  • 19
    • "Is there a particular reason that static methods cannot be overridden" .

    让我通过填写定义为你重新提出这个问题 .

    • "Is there a particular reason that methods resolved at compile time cannot be resolved at runtime."

    或者,更完整地说,如果我想调用没有实例的方法,但是知道类,我怎样才能根据我没有的实例来解析它 .

  • 0

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

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

  • 0

    解决这个问题:错误:缺少方法体,或声明abstract static void main(String [] args);

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

    输出:20

    现在我们可以在界面中使用静态方法了

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

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

  • 2

    可以实现的是静态接口(而不是接口中的静态方法) . 实现给定静态接口的所有类都应该实现相应的静态方法 . 您可以使用任何Class clazz获取静态接口SI

    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静态方法实现!动态调度是必要的,您可以通过扩展它来覆盖类的静态方法(如果不是最终的)(当通过静态接口调用时) . 显然,这些方法只能访问其类的静态变量 .

  • 2

    首先,所有语言决策都是语言创建者做出的决定 . 在软件工程或语言定义或编译/解释器编写领域,没有任何内容表明静态方法不能成为接口的一部分 . 我为他们创建了几种语言和编写的编译器 - 它们只是坐下来定义有意义的语义 . 我认为接口中静态方法的语义非常清楚 - 即使编译器必须将方法的解析推迟到运行时 .

    其次,我们一直使用静态方法有一个有效的理由,有一个包含静态方法的接口模式 - 我不能代表任何人,但我定期使用静态方法 .

    最可能的正确答案是,在定义语言时,没有感觉到需要接口中的静态方法 . 多年来Java已经发展了很多,这个项目显然已经获得了一些兴趣 . 对于Java 7来说,它表明它已经上升到可能的兴趣水平导致语言改变 . 当我不再需要实例化一个对象时,我会很高兴我可以调用我的非静态getter方法来访问子类实例中的静态变量...

  • 468

    这已经被问到并回答了,here

    要复制我的答案:

    在接口中声明静态方法永远不会有意义 . 它们不能通过正常调用MyInterface.staticMethod()执行 . 如果通过指定实现类MyImplementor.staticMethod()来调用它们,那么您必须知道实际的类,因此接口是否包含它是无关紧要的 .

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

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

    静态规则说必须执行声明的var类型中定义的方法 . 由于这是一个界面,这是不可能的 .

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

    虽然你可以说这相当于“因为Java就是这样做的”,但实际上这个决定是其他设计决策的逻辑结果,也是出于很好的理由 .

  • 0

    假设你可以做到;考虑这个例子:

    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.
      }
    
    }
    
  • 5

    几个答案讨论了可覆盖静态方法概念的问题 . 但是有时候你遇到的模式看起来就像你想要使用的那样 .

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

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

    并按ID加载一个人

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

    这很方便,但它有问题;值得注意的是,静态方法的存在不能在接口中强制执行 . 接口中的可覆盖静态方法正是我们所需要的,只要它能以某种方式工作 .

    EJB通过Home接口解决了这个问题;每个对象都知道如何找到它的Home和Home包含“静态”方法 . 这样可以根据需要覆盖“静态”方法,并且不会使用不适用于bean实例的方法使正常(称为“远程”)接口混乱 . 只需使普通接口指定“getHome()”方法即可 . 返回Home对象的实例(我想这可能是单例),调用者可以执行影响所有Person对象的操作 .

  • 4

    界面永远不能静态解除引用,例如 ISomething.member . 始终通过引用接口子类实例的变量取消引用接口 . 因此,如果没有子类的实例,接口引用永远不会知道它引用哪个子类 .

    因此,接口中与静态方法的最接近的近似是忽略"this"的非静态方法,即不访问实例的任何非静态成员 . 在低级抽象中,每个非静态方法(在任何vtable中查找之后)实际上只是一个具有类范围的函数,它将"this"作为隐式形式参数 . 见Scala's singleton object and interoperability with Java作为该概念的证据 . 因此,每个静态方法都是一个类范围不带"this"参数的函数 . 因此,通常静态方法可以静态调用,但如前所述,接口没有实现(是抽象的) .

    因此,为了使接口中的静态方法最接近,就是使用非静态方法,然后不访问任何非静态实例成员 . 任何其他方式都没有可能的性能优势,因为没有办法静态链接(在编译时) ISomething.member() . 我在接口中看到静态方法的唯一好处是它不会输入(即忽略)隐式"this",因此不允许访问任何非静态实例成员 . 这将隐含地声明,不访问"this"的函数是不变的,甚至对于其包含的类也不是只读的 . 但是在 ISomething 接口中声明"static"也会使尝试使用 ISomething.member() 访问它的人感到困惑,这会导致编译器错误 . 我想如果编译器错误是充分解释的,那么比尝试教育人们使用非静态方法来完成他们想要的东西(显然主要是工厂方法)更好,就像我们在这里做的那样(并且已经重复了3次)这个网站的问答时间),这显然是一个对很多人来说不直观的问题 . 我必须考虑一段时间才能得到正确的理解 .

    在接口中获取可变静态字段的方法是在接口中使用非静态getter和setter方法来访问子类中的静态字段 . 旁注,显然不可变的静态可以在 static final 的Java接口中声明 .

  • 30

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

    Actually you can in Java 8.

    根据Java doc

    静态方法是一种与定义它的类相关联的方法,而不是与任何对象相关联的方法 . 该类的每个实例都共享其静态方法

    在Java 8中,接口可以具有 default methodsstatic methods . 这使我们更容易在库中组织辅助方法 . 我们可以在同一个接口中保留特定于接口的静态方法,而不是在单独的类中 .

    默认方法示例:

    list.sort(ordering);
    

    代替

    Collections.sort(list, ordering);
    

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

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

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

    public interface IXMLizableFactory<T extends IXMLizable> {
      public T newInstanceFromXML(Element e);
    }
    
    public interface IXMLizable {
      public Element toXMLElement();
    }
    
  • 35

    Java 8允许静态接口方法

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

    这里有两个问题:

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

    • 为什么不能覆盖静态方法?

    接口中的静态方法

    没有强大的技术原因可以解释为什么接口在以前的版本中没有静态方法 . 这是一个重复的问题summed up nicely by the poster . 静态接口方法最初被认为是a small language change,然后有an official proposal在Java 7中添加它们,但它后来被dropped due to unforeseen complications.

    最后,Java 8引入了静态接口方法,以及具有默认实现的覆盖实例方法 . 但它们仍然不能有实例字段 . 这些功能是lambda表达式支持的一部分,您可以在Part H of JSR 335.中阅读有关它们的更多信息 .

    重写静态方法

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

    静态方法在编译时可以解析 . 动态调度对于实例方法是有意义的,其中编译器可以解析要调用的方法 . 但是调用静态方法需要一个类,并且由于该类在编译时是静态已知的,因此动态调度是不必要的 .

    有关实例方法如何工作的一些背景知识是必要的,以了解这里发生了什么 . 我确信实际的实现是完全不同的,但让我解释一下方法调度的概念,它可以准确地模拟观察到的行为 .

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

    如果超类和子类在其表中都有相同方法签名的条目,则首先遇到子类的版本,并且永远不使用超类的版本 - 这是“覆盖” .

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


    构造函数“接口”

    这里有一些材料可以解决最近编辑的问题 .

    听起来你想要为 IXMLizable 的每个实现有效地强制使用类似构造函数的方法 . 忘记尝试使用接口强制执行此操作一分钟,并假装您有一些符合此要求的类 . 你会怎么用?

    class Foo implements IXMLizable<Foo> {
      public static Foo newInstanceFromXML(Element e) { ... }
    }
    
    Foo obj = Foo.newInstanceFromXML(e);
    

    由于必须在"constructing"新对象时明确命名具体类型 Foo ,编译器可以验证它确实具有必要的工厂方法 . 如果没有,那么呢?如果我可以实现缺少"constructor"的 IXMLizable ,并创建一个实例并将其传递给您的代码,那么它就是一个带有所有必要接口的 IXMLizable .

    构造是实现的一部分,而不是接口 . 任何与接口一起成功运行的代码都不关心构造函数 . 任何关心构造函数的代码都需要知道具体的类型,界面也可以忽略 .

  • 0

    您不能在接口中定义静态方法,因为静态方法属于不属于类实例的类,接口不属于类 . Read more here.

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

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

    在这种情况下,你拥有的是两个带有2个不同静态方法的类,名为methodX() .

  • 3

    我认为java没有静态接口方法,因为你不需要它们 . 你可能认为你这样做了,但是......你会如何使用它们?如果你想称他们为

    MyImplClass.myMethod()
    

    那么你不需要在界面中声明它 . 如果你想称他们为

    myInstance.myMethod()
    

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

    接口允许您定义实现接口的类实例与调用代码之间的 Contract . 并且java帮助您确保不违反此 Contract ,因此您可以依赖它并且不必担心哪个类实现此 Contract ,只需“签署 Contract 的人”就足够了 . 如果是静态接口你的代码

    MyImplClass.myMethod()
    

    不依赖于每个接口实现都有这个方法的事实,所以你不需要java来帮助你确保它 .

  • -5

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

  • 2

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

  • 6

    虽然我意识到Java 8解决了这个问题,但我认为我会使用我正在处理的场景(锁定使用Java 7),其中能够在界面中指定静态方法会有所帮助 .

    我有几个枚举定义,我已经定义了“id”和“displayName”字段以及由于各种原因评估值的辅助方法 . 实现接口允许我确保getter方法到位,而不是静态帮助器方法 . 作为一个枚举,确实没有一种干净的方法可以将辅助方法卸载到继承的抽象类或类似的东西中,因此必须在枚举本身中定义方法 . 另外因为它是一个枚举,你永远不能真正将它作为一个实例对象传递并将其视为接口类型,但是能够通过接口要求存在静态帮助器方法是我喜欢的它在Java 8中得到支持 .

    这是代表我的观点的代码 .

    接口定义:

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

    一个枚举定义的示例:

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

    通用枚举实用程序定义:

    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, " , ")));
        }
    }
    
  • 0

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

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

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

    元类用于此目的 . 您可以尝试使用Class类Java . 但问题是Java对此不够灵活 . 您不能在接口的类对象中声明方法 .

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

    ..布拉

    无论如何,你有一个简单的解决方法 - 使用相同的逻辑使方法非静态 . 但是,您必须首先创建一个对象来调用该方法 .

  • 0

    随着 Java 8 的出现,现在可以在接口中编写 defaultstatic 方法 . docs.oracle/staticMethod

    例如:

    public interface Arithmetic {
    
        public int add(int a, int b);
    
        public static int multiply(int a, int b) {
            return a * b;
        }
    }
    
    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);
        }
    }
    

    Result :6

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

相关问题