【问题标题】:Instantiating interfaces in Java在 Java 中实例化接口
【发布时间】:2013-05-20 23:42:25
【问题描述】:

我有这个界面:

public interface Animal {
    void Eat(String name);
}

而这里的代码实现了接口:

public class Dog implements Animal {
    public void Eat(String food_name) {
        System.out.printf(food_name);
    }
    
    public static void main(String args[]) {
        Animal baby2 = new Dog(); // <- this line
        baby2.Eat("Meat");
    }
}

我的问题是,为什么代码有效?无法实例化接口。然而在这种情况下,接口被实例化(用注释标记)。

这里发生了什么?

【问题讨论】:

  • 注意:按照惯例,方法应该以小写字母开头
  • A) 实际上,一个接口可以被实例化......匿名。 B)您不是在实例化接口,而是在实例化Dog
  • @BrianRoach 你在 A 点是什么意思?匿名类是扩展其他类或实现接口的类(不是接口)。它与 OP 示例或instantiating an abstract class 中的情况相同。还是我错过了什么?
  • @Pshemo Runnable r = new Runnable() { public void run() { System.out.println("hi!"); }}; ;) (是的......它是语法糖......它实际上是一个实现 Runnable 的匿名类)
  • @BrianRoach 这将生成实现 Runnable 的匿名类的实例。如果您编译您的代码,您将看到名为xxx$1.class 的附加文件。所以它不会实例化接口,而是实现该接口的匿名类。也尝试打印System.out.println(r.getClass());

标签: java interface abstract


【解决方案1】:

不,不是 - 您正在实例化 Dog,但由于 DogAnimal,您可以将变量声明为 Animal。如果您尝试实例化接口Animal,它将是:

Animal baby2 = new Animal();

尝试一下,然后看着编译器惊恐地尖叫 :)

【讨论】:

  • 那么为什么要实例化为接口,然后使用类初始化呢?至于他们为什么不这样做:Dog baby = new Dog();
  • @user1535147 通常用于public void Foo(Animal bar) 之类的方法中,它将采用实现Animalany 类。
  • 这取决于 - 如果您需要 Dog 中不属于 Animal 合约的特殊功能(想象一个 bark 方法),那么您需要将其声明为 @ 987654332@(或使用演员表,但这不一定好);但在其他情况下,当您仅使用 eat 方法或仅期望 Animal 的子类的实例将其声明为 Animal 就足够了,并且不会尝试使用 Dog(您可以稍后更改为Elephant;此外,它记录了您的意图(嘿,Animals 足够了)。
  • 事实上你可以。 Runnable 是可以实例化的众多接口之一。
  • @feresr 不,你不能。当您“实例化”Runnable 时,实际上是创建了一个实现 Runnable 接口的匿名类的新实例。
【解决方案2】:

Dog 不是一个接口:Dog 是一个,它实现Animal 接口。

这里没有发生任何不愉快的事情。


请注意,您可以实例化接口的匿名实现,如下所示:

Animal animal = new Animal() {
    public void Eat(String food_name) {
        System.out.printf("Someone ate " + food_name);
    }
};

【讨论】:

  • 如果我可以问,这是用来做什么的?
  • @user1535147 看看这里:what-does-it-mean-to-program-to-an-interface
  • 当你不想创建一个独立的类时使用它——你只需要传递一些代码。这是 java 的“闭包”方式。
  • @Pshemo 感谢您的链接。那个有他有趣例子的家伙很酷。
【解决方案3】:

让我们考虑下面的代码:

interface Cookable {
    public void cook();
}

class Food {
    Cookable c = new Cookable() {
     public void cook() {
         System.out.println("anonymous cookable implementer");
        }
      };
 }

前面的代码创建了一个匿名内部类的实例,但在这里,新的即时类是Cookable接口的实现者。请注意,这是您唯一一次看到语法:

new Cookable()

Cookable 是一个接口而不是一个非抽象类类型。想一想: 您不能实例化接口,但这就是代码看起来正在做的事情。但是,当然,它不是实例化Cookable object——它是创建一个新的anonymous implementer of Cookable 的实例。

你可以读到这一行:

   Cookable c = new Cookable(){}

as “声明一个 Cookable 类型的引用变量,很明显,它将引用一个类中的一个对象 实现 Cookable 接口。但是,哦,是的,我们还没有 一个实现 Cookable 的类,所以我们要做一个正确的 在这里,现在。我们不需要这个类的名字,但它会是一个 实现 Cookable 的类,这个花括号开始 新实现类的定义。”

匿名接口实现者需要记住的重要一点——他们只能实现一个接口。根本没有任何机制可以说您的匿名内部类将实现多个接口。事实上,一个匿名内部类甚至不能同时扩展一个类和实现一个接口。 innve 类必须选择作为命名类的子类并且根本不直接实现任何接口或实现单个接口。

所以不要被任何实例化接口的尝试所迷惑,除非是匿名内部类。 以下内容不合法:

Runnable r = new Runnable(); // can't instantiate interface 

而以下是合法的,因为它正在实例化 Runnable 接口的实现者(匿名实现类):

Runnable r = new Runnable() { 
   public void run(){ }
};

你可以阅读我的文章here

【讨论】:

  • 不错的“Cookable 的匿名实现者”。
【解决方案4】:

您在这里观察到的是SOLIDDependency inversion 方面。

您的代码依赖于Animal 合约的抽象,通过实例化它的具体实现。您只是在声明,“我正在实例化 some 对象,但无论该对象实际上是什么,它都将绑定到 Animal 接口的合同。”

以这些声明为例:

List<String> wordList = new LinkedList<>();
Map<Integer, String> mapping = new HashMap<>();

在这两种情况下,列表和地图的主要方面是它们遵循ListMap 的通用合同。

【讨论】:

    【解决方案5】:
    Animal baby2 = new Dog(); //HERE!!!!!!!!!!!!!!!!!!!!!!
    

    你肯定没有实例化 Animal。您只是将 Dog 实例引用给它。 在java中我们可以获取超类引用。

    【讨论】:

      【解决方案6】:

      这是一个多态的例子,看起来你正在创建“动物”对象,但事实并非如此。您正在创建在运行时计算的“狗”对象。“动物”充当合同。接口不能直接实例化,但可以通过向上转换其子类来用作类型。您还可以使用匿名类将对象实例化为“动物”类型。

         Animal baby2 = new Dog(); //upcasting polymorphically
         Animal baby3=new Animal(){
            public void Eat(String food){System.out.println("fdkfdfk"); }
         }
          //You can instantiate directly as anonymous class by implementing all the method of interface
      

      【讨论】:

        【解决方案7】:

        接口Animal不是实例化的,而是由Dog实现的。而Dog是实例化的

        【讨论】:

          【解决方案8】:

          当你说:

          Animal baby2 = new Dog();
          

          引用类型是Animal(接口),它指向一个具体的实现(Dog)。对象类型 Dog 是具体的并且可以被实例化。在这种情况下,只要 Dog hasanimal 指向 Dog。接口中所有方法的具体实现,可以做一个引用类型的

          如果你做了类似的事情,

          Animal baby2 = new Animal(); // here you are actually instantiating
          

          这将是无效的,因为现在您正试图从抽象实现创建具体对象。

          【讨论】:

            【解决方案9】:

            接口 Animal 充当 Dog 类的数据类型。您实际上是在实例化 Dog 类而不是接口或其数据类型。

            【讨论】:

              【解决方案10】:

              拥有更广阔的视野:

              Animal [] Zoo = new Animal[10] ; // is also correct
              

              但是为什么?

              整个想法是,在上面的表格中,您可以放置​​ 10 种不同类型的动物。唯一的条件是所有进入动物园的动物都必须实现接口 Animal 。

              public interface Animal {
               void Eat();
              }
              class Wolf implements Animal {  void Eat (){ 
              System.out.println("Wolf eats meat ") ;}}
              
              Class Zebra implements Animal{ void Eat (){
              System.out.println("Zebra eats the grass ") ;}}
              
              class test {
              public static void main (String args []) {
              
              Animal [] Zoo = new Animal[2] ;
              
              Zoo[0] =  new Wolf() ;
              Zoo[1] = new Zebra() ;
              
               //so you can feed your animals in Zoo like this
              
               for (int i=0 ; i<Zoo.lenght;i++) {Zoo[i].Eat();}
              }
              }
              

              【讨论】:

              • 很高兴指出这个有趣的方面。谷歌做了同样的事情here,这让我困惑了一段时间。
              【解决方案11】:

              您不能实例化接口。可以认为该功能类似于抽象类的功能。您可以引用接口,但不能创建接口对象。如果你做这样的事情......

              动物 a = new Animal(); 编译器将显示错误-“无法实例化类型 Animal”。

              【讨论】:

                【解决方案12】:

                其实你可以实例化接口。这是您可以尝试的代码

                public static void main(String args[]) {
                    System.out.println(new Animal() {
                        public String toString() {
                            return "test";
                        }
                    });
                }
                

                这个程序运行成功并打印出test 试试看。

                【讨论】:

                • 谁给这个解决方案+1?你不能实例化一个接口。当你创建一个匿名类时,实现接口的新“无名”类会被实例化!!!
                【解决方案13】:

                这里只是引用接口,但实例化仅由类完成。 例如

                Animanl a = 新狗 Animal a - 变量被引用 new Dog - 现在已分配内存

                【讨论】:

                  【解决方案14】:

                  Java 8 让你使用,功能接口,

                  @FunctionalInterface // this is not mandatory 
                  interface A{
                      void m1(); // only one abstract method allowed for functional interface
                  }
                  
                  class Main{
                     public static void main(String a[]){
                  
                        // old usage
                        A a1 = new A(){
                          @Override
                          public void m1(){
                             System.out.println("Call Me normally");
                          }
                        };
                  
                        a1.m1();
                  
                        // new in java 8, functional interface
                        A a2 = ()-> System.out.println("Call Me as functional interface");
                        a2.m1();
                   
                     }
                  }
                  

                  【讨论】:

                    【解决方案15】:

                    你所做的是类型转换。你已经创建了一个 dog 类的实例,并将它的类型转换为接口 animal。它是运行时多态的一个例子。但是是的,可以实现一个接口,我在搜索这个时已经到达这里。 即

                    public class demo16{
                    interface cardio{
                        void run();
                    }
                    
                    static void foo(){
                        cardio c = new cardio(){ //HENCE instance of "interface cardio" is being created inside a method foo
                            public void run(){
                                System.out.println("How you doing  ! ");
                            }; //HENCE observe the ";" beside }
                        };     //HENCE observe the ";" beside }
                        c.run();
                    }
                    
                    public static void main(String [] args){
                        foo();
                    }
                    

                    }

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2022-01-06
                      • 1970-01-01
                      • 1970-01-01
                      • 2019-01-03
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多