Java:接口和接口调用的理解

Java:接口和接口调用的理解

前言:

笔者在接触接口的时候比较好理解,但是对接口调用一直是迷迷糊糊,现在学习android开发的时候经常设计java接口调用,现在来填窟窿了,总结一下。

接口

想必接口大家一定不陌生。

  • 接口是一种抽象类型,它只包含方法签名(方法的名称、参数列表和返回类型),而没有方法的实现。在 Java 中,接口使用interface关键字来定义。

    • public interface MyInterface {
          void method1(int parameter);
          String method2();
      }
      

      说白了,java接口就是一个标准,说你小子要是想插入这个接口,就必须要符号我这个接口的规范,我这个接口规定了你那边必须要有和我一样的函数名称、返回类型、参数列表,可以说除了函数体之外一模一样。函数体的实现逻辑就由这个接入的类来重写。

      到这里有人问了,这和子类继承父类重写父类方法有些类似啊,这玩意有啥用啊。见下文。

    与类不同,一个类只能继承一个父类,但一个类可以实现多个接口。简单来嗦,生理上你只能有一个父亲,但是有很多人可以规范你的行为。

  • 常量:接口中除了抽象方法还可以写入常量:这些常量默认是public static final的,即它们是公共的、静态的、不可变的。

    public interface MyInterface {
        int CONSTANT_VALUE = 10;
        void method1(int parameter);
    }
    

在介绍接口回调之前,先复习一下继承的部分关键知识,怕有些同学会混淆记忆。

继承

成员变量

子类父类中不同名的成员变量之间无影响,出现重名变量时子类优先访问自己对象中的成员变量,若要访问父类中非私有的成员变量时使用super关键字.

成员方法

子父类中出现不重名成员方法时调用无影响;子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现

构造方法

继承后子类构方法器特点:子类所有构造方法的第一行都会默认先调用父类的无参构造方法

  • 子类构造方法执行的时候,都会在第一行默认先调用父类无参数构造方法一次。
  • 子类构造方法的第一行都隐含了一个**super()**去调用父类无参数构造方法,**super()**可以省略不写。

在Java中,子类可以使用super关键字来调用父类的构造函数:

class Parent {
    int x;
    
    Parent(int x) {
        this.x = x;
    }
}

class Child extends Parent {
    int y;
    
    Child(int x, int y) {
        super(x);  // 调用父类的构造函数
        this.y = y;
    }
}

上转型与下转型

  • 向上转型:将一个子类对象赋值给一个父类类型的变量,这个称为上转型。举个例子

    class Animal {
        public void eat() {
            System.out.println("Animal is eating");
        }
    }
    class Dog extends Animal {
        @Override
        public void eat() {
            System.out.println("Dog is eating");
        }
        public void bark() {
            System.out.println("Dog is barking");
        }
    }
    
    • 此时上转型即为:
    Animal a = new Dog()
    

    直白点说,以父类类型来定义这个变量,就是告诉编译器我这个变量是父亲类型的,所以只能调用子类中重写父类的同名方法或者父类的方法,其他特殊方法是找不到的哦。

    • 通过变量a调用方法时,只能调用Animal类中定义的方法或者在Dog类中重写Animal类的方法。例如,a.eat()会调用Dog类中重写后的eat方法,输出Dog is eating。但是,不能通过a直接调用Dog类中特有的方法,如bark方法。
    • 如果有多个子类都重写了Animal类的eat方法,当通过Animal类型的变量引用不同子类对象时,会执行相应子类中重写后的eat方法。这个就是所谓的多态了。
    • 当子类没有重写父类的方法时,调用了该方法,则编译器会沿着继承链向上查找,直到找到方法定义或者到达Object类。因此,上转型这个模式非常适合写具有多次继承的分层结构。
  • 向下转型:

    • 直接向下转型会报错,需要先向上转型后再向下转型。有人会问,这样做的意义是??

    • 其实先上转型,让方法可以接受更多的对象类型用于在多态场景中使用,如下:

      public void makeAnimalSound(Animal animal) {
          if (animal instanceof Dog) {
              Dog dog = (Dog)animal;
              dog.bark();
          } else if (animal instanceof Cat) {
              Cat cat = (Cat)animal;
              cat.meow();
          }
      }
      
    • 当需要使用某些类的特有方法时,再下转型

      接口回调

先上概念:

  • 接口回调是一种设计模式,它允许一个对象(通常称为客户端)将一个接口的实现传递给另一个对象(通常称为服务提供者)。服务提供者在适当的时候会调用这个接口中的方法,从而实现回调。

有点绕,简单来说,就是我一个方法的参数类型是一个接口,如shape,而这个接口的具体实现呢是一个对象里面写的。

public interface Shape {
    void draw();
}
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}
public class GraphicsEngine {
    public void drawShape(Shape shape) {
        shape.draw();
    }
}

public class Main {
    public static void main(String[] args) {
        GraphicsEngine engine = new GraphicsEngine();
        Shape circle = new Circle();
        //一开始疑惑这个用接口类型来声明的,其实是把实现了某个接口的具体类的对象赋值给该接口类型的变量。这样实现多态,具有相同名称的接口的类都可以这样赋值对象,实现接口回调
        engine.drawShape(circle);
    }
}

现实应用:

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage primaryStage) {
        Button button = new Button("Click me");
        // 定义事件处理逻辑
        //函数式接口 就一个方法
        EventHandler<ActionEvent> eventHandler = new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("Button clicked");
            }
        };
        button.setOnAction(eventHandler);
        VBox layout = new VBox(10);
        layout.getChildren().add(button);
        Scene scene = new Scene(layout, 300, 250);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

几个重点:

  • 匿名内部类:在定义类的同时创建该类的一个实例,过程中不出现新类的名称。假设不使用匿名内部类方法,需要实现一个类extends一个接口,然后实现方法,很繁琐。

    //匿名内部类,直接实现接口方法
    EventHandler<ActionEvent> eventHandler = new EventHandler<ActionEvent>() {
        //<ActionEvent>泛型 ()创建实例
        @Override
        public void handle(ActionEvent event) {
            System.out.println("Button clicked");
        }
    };
    
  • 接口回调:接受一个EventHandler<ActionEvent>接口类型的参数。EventHandler<ActionEvent>接口定义了一个handle方法,用于处理动作事件。

    button.setOnAction(eventHandler);
    
  • 泛型:在 EventHandler<ActionEvent> eventHandler = new EventHandler<ActionEvent>() {... }; 这段代码中,EventHandler<ActionEvent> 是一种接口类型声明。EventHandler 本身是一个接口,在 JavaFX 等 GUI 编程环境中,用于处理动作事件(ActionEvent),它定义了处理这类事件所需要遵循的规范,也就是通过其中抽象的 handle 方法来响应事件。

​ 具体说说泛型:泛型是 Java 语言中的一种特性,它允许在定义类、接口、方法时使用类型参数,使得这些类型在使用时可以被指定为具体的类型,从而增强代码的通用性、安全性以及可读性。通过泛型,可以编写能够处理多种不同类型数据的代码,同时在编译阶段就能避免一些类型不匹配的错误

举个例子:

//类、接口和方法中,使用尖括号 <> 包围类型参数
//泛型
public class Box<T> {
    private T content;
    public void setContent(T content) {
        this.content = content;
    }
    public T getContent() {
        return content;
    }
}

//实例化
Box<String> stringBox = new Box<>();
//这里 Box<String> 表示 Box 类的类型参数 T 被指定为 String 类型。

接口回调和子类继承同一个父类调用相同方法区别就是不需要所有类去继承同一个父类,只要有一个相同接口即可,非常灵活和实现解耦。具体一点,假设我想要一些不同父类底下的子类具有某一相同的方法,不需要把这些子类的父类再继承同一个父类来写方法

再以以上的button按钮举例,我想要new出来的button有可自定义内容的handle方法,直接用匿名内部类实现即可。

参考:

[1] java 泛型类 如何new | PingCode智库

[2] https://www.doubao.com/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值