实现接口创建对象时,为什么用接口来引用对象而不是类

今天看见这一行代码我迷茫了…

Map<String, String> errors = new HashMap<>();

为什么我非要用Map接口接收errors对象而不是HashMap?接下来我将把试图说服自己的过程展示出来供大家参考。
注:AI有些回答虽然被我尽可能缩减但这这篇文章依然很长,做好心理准备

我:

java中实现类实现了接口,创建对象为什么要写成

接口 对象名 = new 类名 而不是 子类对象 对象名 = new 类名

AI:

好处:多态性,封装,可扩展性
假设你正在开发一个学校管理系统,其中需要处理不同类型的用户,比如教师(Teacher)和学生(Student),他们都有一些共同的行为,比如登录系统、查看个人信息等。这时,你可以定义一个接口User来表示用户的通用行为:

interface User {
    String getUsername();
    String getPassword();
    void login();
    void viewProfile();
}

然后,分别实现这个接口的Teacher和Student类:

class Teacher implements User {
    private String username;
    private String password;

    public Teacher(String username, String password) {
        this.username = username;
        this.password = password;
    }
    @Override
    public String getUsername() {
        return username;
    }
    @Override
    public String getPassword() {
        return password;
    }
    @Override
    public void login() {
        // 教师登录逻辑
    }
    @Override
    public void viewProfile() {
        // 教师查看个人资料逻辑
    }
}
class Student implements User {
    private String username;
    private String password;
    
    public Student(String username, String password) {
        this.username = username;
        this.password = password;
    }
    @Override
    public String getUsername() {
        return username;
    }
    @Override
    public String getPassword() {
        return password;
    }
    @Override
    public void login() {
        // 学生登录逻辑
    }
    @Override
    public void viewProfile() {
        // 学生查看个人资料逻辑
    }
}

接下来,当你需要编写一个管理用户登录的模块时,可以使用User接口类型来声明变量,这样无论处理的是教师还是学生,都可以通过统一的接口方法来操作,而无需关注具体的实现细节:

public class LoginManager {
    public void loginUser(User user) {
        user.login();
        // 登录后处理逻辑,比如记录日志等
    }
}

使用场景示例:

public static void main(String[] args) {
    User teacher = new Teacher("teacher1", "password1");
    LoginManager loginManager = new LoginManager();
    
    // 尽管声明为User类型,但可以传入Teacher或Student实例
    loginManager.loginUser(teacher);
    // 同样可以处理学生登录
    User student = new Student("student1", "password2");
    loginManager.loginUser(student);
}

在这个例子中,通过使用接口User作为变量类型,LoginManager类的loginUser方法可以透明地处理教师和学生的登录操作,无需知道具体是哪种类型的用户,大大提高了代码的灵活性和可扩展性。如果未来还需要添加新的用户类型(比如管理员Admin),只要实现User接口,就可以直接在现有的系统中使用,无需修改已有的登录逻辑。

我:

Teacher teacher = new Teacher("teacher1", "password1");
 LoginManager loginManager = new LoginManager();

这种情况这样也行啊,为什么要用User接口接收

AI:

确实,直接使用具体类如Teacher teacher = new Teacher("teacher1", "password1");进行实例化也是可行的,特别是在那些上下文中,你确切知道要处理的具体类型,并且不需要利用多态特性时。但是,采用接口(如User user = new Teacher("teacher1", "password1");)作为变量类型,尤其是在设计更复杂的系统或模块时,提供了几个显著的优势.

让我们继续使用之前的学校管理系统例子,假设我们有一个DisplayUserProfile类,它负责显示用户的基本信息。如果我们直接使用具体类,代码可能会是这样的:

public class DisplayUserProfile {
    public void display(Teacher teacher) {
        System.out.println("Teacher's Name: " + teacher.getUsername());
        // 其他教师特定的展示逻辑
    }
}
// 使用
Teacher teacher = new Teacher("teacher1", "password1");
DisplayUserProfile display = new DisplayUserProfile();
display.display(teacher);

在这种情况下,DisplayUserProfile类只能处理Teacher对象。如果我们要处理Student对象,我们需要另一个方法,如display(Student student),这会导致代码重复和冗余。
现在,如果我们使用接口User,情况会有所不同:

public class DisplayUserProfile {
    public void display(User user) {
        System.out.println("Username: " + user.getUsername());
        // 根据User接口的通用属性和行为进行展示逻辑
        // 如果需要特定类型的额外信息,可以使用instanceof检查或类型转换
    }
}
// 使用
User teacher = new Teacher("teacher1", "password1");
DisplayUserProfile display = new DisplayUserProfile();
display.display(teacher);

User student = new Student("student1", "password1");
display.display(student);

在这个例子中,DisplayUserProfile类可以处理任何实现了User接口的对象,无论是Teacher还是Student。如果需要处理特定类型的信息(如教师的职称或学生的年级),可以使用instanceof关键字检查用户类型,或者安全地转换类型。这种方式让代码更灵活,减少了复制粘贴和重复的代码,同时也为未来的扩展提供了便利。
如果以后需要添加新的用户类型,如Staff,只需要创建一个Staff类实现User接口,DisplayUserProfile类无需任何修改就能处理Staff对象。这就是使用接口带来的优势。

我:

Map<String, String> errors = new HashMap<>();HashMap<String, String> errors = new HashMap<>();这样使用有没有区别呢

AI:

在实际的Java代码中,Map<String, String> errors = new HashMap<>();HashMap<String, String> errors = new HashMap<>(); 在功能上没有区别。它们都会创建一个新的哈希映射(HashMap),其中键(key)和值(value)都是String类型。
然而,它们在编程实践中体现了不同的设计哲学:

  1. 使用Map<String, String>:
    这种做法强调了抽象。变量errors被声明为Map接口类型,意味着你只依赖Map接口提供的公共方法,而不关心具体的实现细节。这样做的好处是,如果将来需要更改映射实现(比如改为LinkedHashMap以保持插入顺序),只需要更改实例化部分,而不会影响使用Map接口的方法。
  2. 使用HashMap<String, String>:
    这种做法更具体,直接声明了HashMap类型。这在你知道需要哈希映射的高效查找性能,并且不打算更换实现时是合适的。如果代码中需要依赖HashMap特有的行为(如无序性、非同步等),那么直接使用HashMap是明确的。

在大多数情况下,推荐使用Map<String, String>,因为这样可以保持代码的抽象性和可扩展性。只有在明确需要HashMap的特性(如性能、无序性等)时,才直接使用HashMap。

总结

很遗憾,我没能说服自己,所以我把它改成HashMap<String, String> errors = new HashMap<>();等踩坑再说。

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值