【Java 类与对象】多态

news2024/9/21 19:02:04

空山新雨后

天气晚来秋


目录

多态的概念

多态实现条件

多态的转型

向上转型

向下转型

instanceof 关键字

方法的重写

@Override注解

重写的权限

只能重写继承而来的方法(1)

 final、static 不能被重写(2)

重写的方法不能带有等级更严格的修饰符(3) 

构造方法不能被重写(4)

在子类中通过 super 可以调用父类的重写(5)

重载与重写

静态绑定与动态绑定

动态绑定的概念:

静态绑定的概念:

多态的运用

多态在实际生活中的运用

多态在代码练习中的运用

能够降低代码的重复性

可扩展能力更强

多态的概念

多态的概念:

<1> 多态是继封装、继承之后,面向对象的第三大特性

<2> 通俗来说,多态就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态

这里举个例子:

人吃东西是一个很广泛的概念,但是我们的学生与教师是不同的两种形态,当它们去吃饭的时候就有不同的待遇:教师可以吃教师餐,而学生只能吃学生餐 -- 这就是多态。而它们这种同名的动作(吃),但是实现的方法不一样(学生餐、教师餐)的操作就叫做 -- 重写

多态实现条件

在 java 中要实现多态,必须要满足如下几个条件,缺一不可:

<1> 必须在继承体系下
<2> 子类必须要对父类中方法进行重写
<3> 通过父类的引用调用重写的方法

class Person {
    protected String name;
    protected int age;
    protected int id;

    public Person(String name, int age, int id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

    public void eat(){
        System.out.println("正在吃");
    }

}

class Student extends Person {

    public Student(String name, int age, int id) {
        super(name, age, id);
    }

    public void eat(){
        System.out.println(this.name + " 正在吃学生餐");
    }

}

class Teacher extends Person {

    public Teacher(String name, int age, int id) {
        super(name, age, id);
    }

    public void eat(){
        System.out.println(this.name + " 正在吃教师餐");
    }

}

class Test {
    public static void eat(Person a){
        a.eat();
    }
    public static void main(String[] args) {
            Student cat = new Student("张三",18,2024123);
            Teacher dog = new Teacher("李四", 38,2024567);
            eat(cat);
            eat(dog);
        System.out.println("--------------------------------------------");
            Person s2 = new Student("王五",18,2024234);
            s2.eat();
            Person t2 = new Teacher("赵六",38,2020789);
            t2.eat();
    }
}

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法 

 我们的调用方法一般有两种:

 《1》

public static void eat(Person a){
        a.eat();
    }
    public static void main(String[] args) {
            Student cat = new Student("张三",18,2024123);
            Teacher dog = new Teacher("李四",38,2024567);
            eat(cat);
            eat(dog);
            //----------------------------------------------------------------
            //eat(new Student("张三",18,2024123));
            //eat(new Teacher("李四", 38,2024567));
    }

这种方法比较符合实际的开发,因为一个大型项目都不是一个人写出来的:一般是由一些人写类,一些人再写调用类的功能

再提多态: 

当类的调用者在编写 eat 这个方法的时候,参数类型为 Person (父类),此时在该方法内部并不知道,也不关注当前的 a 引用指向的是哪个子类的实例。此时 a 这个引用调用 eat 方法可能会有多种不同的表现(和 a 引用的实例相关),这种行为就称为多态

 《2》

    public static void main(String[] args) {
            Person s2 = new Student("王五",18,2024234);
            s2.eat();
            Person t2 = new Teacher("赵六",38,2020789);
            t2.eat();
    }

这个就涉及到多态的转型 -- 向上转型,我们接下来就来谈谈多态的转型

多态的转型

向上转型

本质:父类的引用指向子类的对象

特点:

编译类型看左边,运行类型看右边
可以调用父类的所有成员(需遵守访问权限)
不能调用子类的特有成员
运行效果看子类的具体实现
格式:
父类类型 引用名 = new 子类类型();

其他都好理解,我们这里来说一下第三点:不能调用子类的特有成员

class Person {
    protected String name;
    protected int age;
    protected int id;
    public Person(String name, int age, int id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }
    public void eat(){
        System.out.println("正在吃");
    }
}

class Student extends Person {
    public String interest;
    public void interest(){
        System.out.println("游戏");
    }
    public Student(String name, int age, int id) {
        super(name, age, id);
    }
    public void eat(){
        System.out.println(this.name + " 正在吃学生餐");
    }
}


class Test {
    public static void eat(Person a){
        a.eat();
    }
    public static void main(String[] args) {
        Person s1 = new Student("王五",18,2024234);
        s1.eat();
        System.out.println(s1.interest);
        s1.interest();
    }
}

这里说一下我对这个的理解:我们 C语言 阶段不是学过类型转化吗?我们的 double 转化成 int 会损失精度,这里也一样:首先它们是继承关系,子类中拥有父类的所有成员属性,但是也有自己特有的属性。多态就是将子类与父类相同的属性 “分割” 给父类,这样我们调用父类的引用就能调用子类特有的方法,而父类中没有的属性则不能被调用(相当被丢失的精度)

向上转型的优点:让代码实现更简单灵活 
向上转型的缺陷:不能调用到子类特有的方法

向下转型

将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换 

class Animal {
    public static String name;
    public int age;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println("正在吃");
    }
}

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(this.name+" 吃狗粮");
    }
    public void bark(){
        System.out.println(this.name+" 旺旺叫");
    }
}

public class Test{
    public static void main(String[] args) {
        Animal a = new Dog("大黄",10);
        Dog g = (Dog)a;
        g.eat();
    }
}

注意:你不能把当前子类的父类引用给别的子类向下转型

 

class Animal {
    public static String name;
    public int age;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println("正在吃");
    }
}

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(this.name+" 吃狗粮");
    }
    public void bark(){
        System.out.println(this.name+" 旺旺叫");
    }
}

class Cat extends Animal{
    public Cat(String name, int age){
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(this.name+"吃鱼");
    }
}

public class Test{
    public static void main(String[] args) {
        Animal a = new Dog("大黄",10);
        Cat c = (Cat)a;
        c.eat();
    }
}

虽然代码并没有报错但是编译的时候会错出异常:

这里解释一下:父类对象 a 引用了 Dog 的子类对象,但是拿着这个 Dog 引用的父类对象向下转型给了 Cat 的子类对象,猫不是狗!!!

instanceof 关键字

instanceof 运算符是 Java 中的一种类型判断运算符,用于检查一个对象是否是一个类的实例 ,如果是返回 ture ,不是则返回 false

所以向下转型是有一定风险的,所以我们在使用的时候需要再判断一下

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java 中为了提高向下转型的安全性,引入 了 instanceof,如果该表达式为 true,则可以安全转换

Animal a = new Dog("大黄",10);
        if(a instanceof Cat){
            Cat c = (Cat)a;
            c.eat();
        }else{
            System.out.println("a instanceof Cat not!");
        }

 为了安全考虑,我们在使用向下转型时,都应该判断一下

 

方法的重写

重写的概念:

如果子类具有和父类一样的方法,我们称之为方法重写。 方法重写用于提供父类已经声明的方法的特殊实现,是实现多态的基础条件

重写的条件:

方法名相同
参数相同(个数、顺序、类型)、返回值类型相同
必须是继承关系

@Override注解

@Override 是重写的注解,其主要作用是检查该方法是否构成重写:

public void eat1(){
        System.out.println(this.name + " 正在吃学生餐");
    }

如上图:我们父类中并没有 eat1() 这个方法,构成不了重写,所以就报了这个错误

重写的权限

只能重写继承而来的方法(1)

因为重写是在子类重新实现从父类继承过来的方法时发生的,所以只能重写继承过来的方法。这意味着,只能重写那些被 public、protected 或者 default 修饰的方法,private 或者修饰的方法无法被重写

 错误示范:

class Animal {
    public String name;
    public int age;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private void eat(){
        System.out.println("正在吃");
    }
}

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(this.name+" 吃狗粮");
    }
}

public class Test{
    public static void main(String[] args) {
        Animal a = new Dog("大黄",10);
        a.eat();
    }
}

 final、static 不能被重写(2)

<1> 因为 final 修饰的方法都是【封闭】方法不能被重写

<2> 重写的目的在于父类引用可以根据子类对象的运行时实际类型不同而调用不同实现代码,从而表现出多态。但是 static 静态方法不需要借助引用就可以调用

错误示范:

class Animal {
    public static String name;
    public int age;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    static void interest(){
        System.out.println("在睡觉");
    }

    final void eat(){
        System.out.println("正在吃");
    }
    
}

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(this.name+" 吃狗粮");
    }
    @Override
    static void interest(){
        System.out.println("狗睡觉");
    }
    
}

public class Test{
    public static void main(String[] args) {
        Animal a = new Dog("大黄",10);
        a.eat();
    }
}

重写的方法不能带有等级更严格的修饰符(3) 

如果子类被重写,那么子类的访问修饰符一定要大于等于父类的权限

修饰符权限大小排序:

private < 包访问权限 < protected < public

构造方法不能被重写(4)

因为构造方法很特殊,而且子类的构造方法不能和父类的构造方法同名(类名不同),所以构造方法和重写之间没有任何关系

在子类中通过 super 可以调用父类的重写(5)

class Person {
    protected String name;
    protected int age;
    protected int id;
    public Person(String name, int age, int id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }
    public void eat(){
        System.out.println("正在吃");
    }
    public void interest(){
        System.out.println("玩游戏");
    }
}

class Student extends Person {
    public String interest;
    public void interest(){
        super.eat();
        System.out.println(this.name+" 玩游戏");
    }
    public Student(String name, int age, int id) {
        super(name, age, id);
    }
    @Override
    public void eat(){
        System.out.println(this.name + " 正在吃学生餐");
    }
}


class Test {
    public static void eat(Person a){
        a.eat();
    }
    public static void main(String[] args) {
        Person s1 = new Student("王五",18,2024234);
        s1.eat();
        System.out.println("---------------------------------------------");
        s1.interest();
    }
}

重载与重写

重载:

重载只存在于继承,是子类与父类之间的一种多态体现,重载的方法名、参数列表、返回值必须相同

重写:

重写存在于类中,是一个类的多态体现,重写的方法名必须相同,参数列表、返回值至少有一个不同

区别点重写(override)重载(override)
参数列表一定不能修改必须修改
返回类型一定不能修改【除非可以构成父子类关系】可以修改
访问限定符一定不能做更严格的限制(可以降低限制)可以修改

静态绑定与动态绑定

在 Java 中,当你调用一个方法时,可能会在编译时期解析,也可能实在运行时期解析,这全取决于到底是一个静态方法还是一个虚方法。如果是在编译时期解析,那么就称之为静态绑定,如果方法的调用是在运行时期解析,那就是动态绑定

Java 是一门面向对象的编程语言,优势就在于支持多态。多态使得父类型的引用变量可以引用子类型的对象

动态绑定的概念:

如果调用子类型对象的一个虚方法,编译器将无法找到真正需要调用的方法,因为它可能是定义在父类型中的方法,也可能是在子类型中被重写的方法,这种情形,只能在运行时进行解析,因为只有在运行时期,才能明确具体的对象到底是什么。这也是我们俗称的运行时或动态绑定

静态绑定的概念:

另一方面,private static 和 final 方法将在编译时解析,因为编译器知道它们不能被重写,所有可能的方法都被定义在了一个类中,这些方法只能通过此类的引用变量进行调用。这叫做静态绑定或编译时绑定。所有的 private static 和 final 方法都通过静态绑定进行解析

动态绑定与静态绑定这两个概念的关系,与 “方法重载”(静态绑定)和 “方法重写”(动态绑定)类似。动态绑定只有在重写可能存在时才会用到,而重载的方法在编译时期即可确定:
 

总而言之,其区别如下:

静态绑定在编译时期,动态绑定在运行时期
静态绑定只用到类型信息,方法的解析根据引用变量的类型决定,而动态绑定则根据实际引用的的对象决定
在 java 中,private static 和 final 方法都是静态绑定,只有虚方法才是动态绑定

多态是通过动态绑定实现的

多态的运用

多态在实际生活中的运用

我拿手机迭代的方式举个例子:

若干年前的手机,只能打电话,发短信,来电显示只能显示号码,而今天的手机在来电显示的时候,不仅仅 可以显示号码,还可以显示头像,地区等。在这个过程当中,我们不应该在原来老的类上进行修改,因为原来的类,可能还在有用户使用,正确做法是:新建一个新手机的类,对来电显示这个方法重写就好了,这样就达到了我们当今的需求了


这是不是跟我们的重写很相像:重新定义一个新的类,来重复利用其中共性的内容, 并且添加或者改动新的内容

多态在代码练习中的运用

能够降低代码的重复性

这里用代码来举个例子:
在没有学多态以前,我们碰到一个需要判断在执行调用那个方法的操作往往是通过 if-else 来实现的。如图所示:

class Shape {
    public void draw() {
        System.out.println("画图形!");
    }
}

class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("画一个矩形!");
    }
}
class Cycle extends Shape{
    @Override
    public void draw() {
        System.out.println("画一个圆圈!");
    }
}

class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("画一个三角形!");
    }
}

class Flower extends Shape {
    @Override
    public void draw() {
        System.out.println("画一朵花!");
    }
}

public class Test2 {
    public static void drawMap(Shape shape) {
        shape.draw();
    }

    public static void drawMaps1() {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Triangle triangle = new Triangle();

        String[] shapes = {"1", "2", "2", "1", "3"};

        for(String s : shapes) {
            if(s.equals("1")) {
                cycle.draw();
            }else if(s.equals("2")) {
                rect.draw();
            }else if(s.equals("3")) {
                triangle.draw();
            }
        }
    }

    public static void main(String[] args) {
        drawMaps1();
    }

}

这样写的话你会发现十分的繁琐,现在你学会了多态你变可以这么写:

public class Test2 {
    public static void drawMap(Shape shape) {
        shape.draw();
    }

    public static void drawMaps() {
        Rect rect = new Rect();
        Shape shapeCycle = new Cycle();
        Triangle triangle = new Triangle();
        Flower flower = new Flower();

        Shape[] shapes = {shapeCycle,rect,rect,
                shapeCycle,triangle,flower};
        for(Shape shape : shapes) {
            shape.draw();
        }
    }
    public static void main(String[] args) {
        drawMaps();
    }

}

还可以这么写:

    public static void main1(String[] args) {
        drawMap(new Cycle());
        drawMap(new Rect());
        drawMap(new Triangle());
    }

这里是多态的魅力,我们不需要去判断 shape 去调用那个子类,只需引用父类。在程序运行时系统会动态绑定、向上转型,从而我们父类会获得子类中与自身匹配的方法

 

可扩展能力更强

如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低

class rhombus extends Shape {
    @Override
    public void draw() {
        System.out.println("画一个菱形!");
    }
}

对于类的调用者来说(drawShapes方法),只要创建一个新类的实例就可以了,改动成本很低。而对于不用多态的情况,就要把 drawShapes 中的 if - else 进行一定的修改,改动成本更高

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2123469.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

向量——通俗地解释

1. 向量 向量是一个既有大小(模)又有方向的对象&#xff0c;它可以用来描述空间中的位置、力或速度等量。我们可以从物理、数学和计算机的角度来看待向量&#xff0c;这三种观点看似不同却有关联。 &#xff08;1&#xff09;在物理专业视角下&#xff0c;向量是空间中的箭头&a…

KubeBlocks 如何降低管理多种数据库的学习门槛

什么是 KubeBlocks KubeBlocks 是一个开源的 Kubernetes 数据库 operator&#xff0c;能够帮助用户在 Kubernetes 上运行和管理多种类型的数据库。据我们所知&#xff0c;大多数数据库 operator 通常只能管理某种特定类型的数据库&#xff0c;例如&#xff1a; CloudNativePG…

秋招突击——算法练习——9/4——73-矩阵置零、54-螺旋矩阵、48-旋转图像、240-搜索二维矩阵II

文章目录 引言复习新作73-矩阵置零个人实现 54-螺旋矩阵个人实现参考实现 48-旋转图像个人实现参考实现 240-搜索二维矩阵II个人实现参考实现 总结 引言 秋招开展的不是很顺利&#xff0c;还是要继续准备&#xff0c;继续刷算法&#xff01;不断完善自己&#xff0c;希望能够找…

Jupyter notebook配置与使用(安装过程+环境配置+运行实例)

前言 Jupyter Notebook 是一个开放源代码的 Web 应用程序&#xff0c;它允许创建和共享包含实时代码、方程式、可视化和叙述性文本的文档。 主要功能&#xff1a; 交互式计算&#xff1a;用户可以直接在浏览器中编写和执行代码。Markdown 支持&#xff1a;使用 Markdown 格式来…

一道迭代器失效练习题

随便写写 首先学习迭代器失效 传送门 : C—浅谈迭代器失效 学完迭代器失效之后做一道题呗 题目 分析 vector的迭代器为啥会失效 1、插入的时候扩容&#xff0c;转移空间出现野指针 2、删除的时候移动了元素&#xff0c;导致指针没指向正确的元素 list的迭代器为啥会失效 li…

pdf怎么压缩?分享5种压缩PDF文件的方法

pdf怎么压缩&#xff1f;PDF文件的压缩在日常办公和学习中尤为重要&#xff0c;它不仅能够大幅度缩减文件大小&#xff0c;节省宝贵的存储空间&#xff0c;还能加快文件在网络中的传输速度&#xff0c;提升工作效率。特别是在处理包含大量图像或复杂布局的PDF文档时&#xff0c…

Http带消息头两种请求办法

API接口最近经常碰到&#xff0c;协调几个乙方来回对接&#xff0c;把我折腾晕了&#xff0c;索性自己写一个小的工具&#xff0c;导入历史数据。 获取平台免登录token 接口说明 URL Path&#xff1a;gateweb/bigm-dm/openApi/ologin/openLogin 说明&#xff1a;第三方免登…

vue2 wavesurfer.js(7.8.5)简单使用

文档地址&#xff1a;https://wavesurfer.xyz/docs/ <template><div><el-row><el-card class"card"><div id"waveform" ref"waveform"></div></el-card></el-row><div>总时长&#xff1…

004——双向链表和循环链表

目录 双向链表 双向链表的初始化&#xff08;与单链表类似&#xff09; 增&#xff1a; Ⅰ&#xff09;头插法 Ⅱ&#xff09;尾插法 Ⅲ&#xff09;中间插入 删 改 查 整体代码示例&#xff1a; 循环链表 循环单链表 ​编辑 循环双链表 双向链表 不同于单链表&…

亲测可用导航网站源码分享 – 幽络源

幽络源为大家分享一套经过亲测可用的导航网站源码。初看这套PHP源码时&#xff0c;其数据库结构更像是商城系统源码&#xff0c;但经过某位小天才的修改&#xff0c;它已变成一个功能完备的导航网站。经过站长的测试&#xff0c;该源码运行良好&#xff0c;简单部署即可使用&am…

基于springboot的在线租房系统设计与实现

项目描述 这是一款基于springboot的在线租房系统 截图

438.找到字符串中所有字母异位词

题目 链接&#xff1a;leetcode链接 思路分析&#xff08;滑动窗口&#xff09; 很容易想到&#xff0c;这个题目要求我们在字符串s中找到一个定长的窗口让窗口里面出现异位词。 OK&#xff0c;先思考一下怎么快速判断两个字符串是否是异位词&#xff1f; 比较简单的方法是…

AV1 Bitstream Decoding Process Specification:约定

原文地址&#xff1a;https://aomediacodec.github.io/av1-spec/av1-spec.pdf没有梯子的下载地址&#xff1a;AV1 Bitstream & Decoding Process Specification摘要&#xff1a;这份文档定义了开放媒体联盟&#xff08;Alliance for Open Media&#xff09;AV1视频编解码器…

ubuntu配置tftp、nfs

tftp配置 tftp是简单文件传输协议&#xff0c;基于udp实现传输。这里的简单文件&#xff0c;指的是不复杂、开销不大的文件。 先在ubuntu中安装tftp&#xff0c;输入命令&#xff1a;sudo apt-get install tftp-hpa tftpd-hpa。 接着配置tftp。 输入命令&#xff1a;sudo v…

div内英文不换行问题以及解决方案

div内英文不换行问题以及解决方案 div盒子中文字换行问题&#xff1a;div中放中文的代码&#xff1a;div中放英文的代码&#xff1a; 解决办法注意 div盒子中文字换行问题&#xff1a; div设置宽度以后&#xff0c;如果div中放的是中文&#xff0c;默认文字超过div宽度会自动换…

GAF-PCNN-BiLSTM、GASF-CNN-BiLSTM、GADF-CNN-BiLSTM的多特征分类预测/故障诊断

GAF-PCNN-BiLSTM、GASF-CNN-BiLSTM、GADF-CNN-BiLSTM的多特征分类预测/故障诊断 目录 GAF-PCNN-BiLSTM、GASF-CNN-BiLSTM、GADF-CNN-BiLSTM的多特征分类预测/故障诊断分类效果格拉姆矩阵图 基本介绍程序设计参考资料 分类效果 格拉姆矩阵图 基本介绍 1.Matlab实现GAF-PCNN-Bi…

Kerberos:更安全的网络认证协议

简介 Kerberos 是一种网络认证协议&#xff0c;主要用于特定的场景下&#xff0c;代替传统的token方式&#xff0c;以一种更繁琐&#xff0c;但更安全的方式来认证用户信息。它通过票据 (ticket) 机制&#xff0c;确保用户在网络中与服务之间进行加密通信&#xff0c;并且避免…

【云备份】可视化客户端----QT开发➕QT数据库编程

文章目录 一、 需求分析二、 概念结构设计三、逻辑结构设计1. 用户表 (users)2. 客户端本地文件信息表 (upload_files)3. 备份记录表 (backup_records)4. 服务端备份文件信息表 (backup_files) 四、 开发工具五、具体实现&#xff08;一&#xff09; 客户端程序运行演示 一、 需…

7.科学计算模块Numpy(4)ndarray数组的常用操作(二)

引言 书接上回&#xff0c;numpy能作为python中最受欢迎的数据处理模块&#xff0c;脱离不了它最核心的部件——ndarray数组。那么&#xff0c;我们今天就来了解一下numpy中对ndarray的常用操作。 通过阅读本篇博客&#xff0c;你可以&#xff1a; 1.掌握ndarray数组的分割 …

shader 案例学习笔记之smoothstep函数

参考&#xff1a;smoothstep 用来生成0-1的平滑过渡值 smoothstep函数源码实现&#xff1a; float smoothstep(float t1, float t2, float x) {// Scale, bias and saturate x to 0..1 rangex clamp((x - t1) / (t2 - t1), 0.0, 1.0); // Evaluate polynomialreturn x * x *…