我创建的以下 map 之间有什么不同(在另一个问题中,人们似乎可以互换地使用它们来回答,我想知道它们是否/如何不同):

HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();

推荐答案

物体之间没有区别;在这两种情况下你都有HashMap<String, Object>分.你对这个物体的感知力是不同的.在第一种情况下,接口是HashMap<String, Object>,而在第二种情况下,接口是Map<String, Object>.但底层对象是相同的.

使用Map<String, Object>的好处是,您可以将底层对象更改为不同类型的映射,而不必 destruct 与使用它的任何代码的约定.如果您将其声明为HashMap<String, Object>,那么如果您想更改底层实现,就必须更改合同.


例如:假设我写了这门课:

class Foo {
    private HashMap<String, Object> things;
    private HashMap<String, Object> moreThings;

    protected HashMap<String, Object> getThings() {
        return this.things;
    }

    protected HashMap<String, Object> getMoreThings() {
        return this.moreThings;
    }

    public Foo() {
        this.things = new HashMap<String, Object>();
        this.moreThings = new HashMap<String, Object>();
    }

    // ...more...
}

The class has a couple of internal maps of string->object which it shares (via accessor methods) with subclasses. Let's say I write it with HashMaps to start with because I think that's the appropriate structure to use when writing the class.

后来,Mary编写了子类化代码.她对thingsmoreThings都有一些需要做的事情,所以她自然地把它们放在一个通用的方法中,她使用了我在getThings/getMoreThings上定义方法时使用的相同类型:

class SpecialFoo extends Foo {
    private void doSomething(HashMap<String, Object> t) {
        // ...
    }

    public void whatever() {
        this.doSomething(this.getThings());
        this.doSomething(this.getMoreThings());
    }

    // ...more...
}

后来,我决定,实际上,如果我用TreeMap而不是Foo中的HashMap,会更好.我更新了Foo,将HashMap改为TreeMap.现在,SpecialFoo不再编译了,因为我违反了合同:Foo以前说它提供了HashMap,但现在它提供了TreeMaps.所以我们现在必须修复SpecialFoo(这种事情可能会在代码库中产生波动).

除非我有一个很好的理由来分享我的实现使用了HashMap(这确实发生了),否则我应该做的是将getThingsgetMoreThings声明为只返回Map<String, Object>,而不是更具体.事实上,除非有充分的理由go 做其他事情,即使在Foo之内,我也应该把thingsmoreThings声明为Map,而不是HashMap/TreeMap:

class Foo {
    private Map<String, Object> things;             // <== Changed
    private Map<String, Object> moreThings;         // <== Changed

    protected Map<String, Object> getThings() {     // <== Changed
        return this.things;
    }

    protected Map<String, Object> getMoreThings() { // <== Changed
        return this.moreThings;
    }

    public Foo() {
        this.things = new HashMap<String, Object>();
        this.moreThings = new HashMap<String, Object>();
    }

    // ...more...
}

请注意,我现在在任何地方都使用Map<String, Object>,只是在创建实际对象时才具体.

如果我这么做了,那么玛丽就会这么做:

class SpecialFoo extends Foo {
    private void doSomething(Map<String, Object> t) { // <== Changed
        // ...
    }

    public void whatever() {
        this.doSomething(this.getThings());
        this.doSomething(this.getMoreThings());
    }
}

...改变Foo不会让SpecialFoo停止编译.

接口(和基类)让我们看到only as much as is necessary,保持了我们在幕后进行适当更改的灵活性.一般来说,我们希望我们的推荐信尽可能的基本.如果我们不需要知道它是HashMap,就说它是Map.

这不是一条盲目的规则,但总的来说,coding to the most general interface不会像编码到更具体的东西那样脆弱.如果我记得的话,我就不会创造出一个Foo分的分数,让玛丽以SpecialFoo分的成绩失败.如果Mary记住了这一点,那么即使我把Foo搞糟了,她也会用Map而不是HashMap声明她的私有方法,而我更改Foo的合同也不会影响她的代码.

有时候你做不到,有时候你必须明确.但是,除非你有理由这么做,否则就要朝着最不具体的界面走.

Java相关问答推荐

泽西岛:退回到不注射的客户"

ActivityCompat.请求收件箱自动拒绝权限

那么比较似乎不是词典学的,尽管doctor 这么说

Mongo DB Bson和Java:在子文档中添加和返回仅存在于父文档中的字段?

在Java中将Charsequence数组更改为String数组或List String<>

Select 按位运算序列

Spring Batch 5-不要让它在数据库中自动创建表

Apache POI:使用反射获取zoom 级别

RESTful框架类字段是安全的还是不安全的

Jakarta CDI强制bean构造/注册遗留事件侦听器

当返回Mono<;Something>;时,不会调用Mono<;void>;.flatMap

当Volatile关键字真的是必要的时候?

我的Spring Boot测试显示&IlLegalStateException:无法加载某事的ApplicationContext.

Win32函数的JNA绑定DwmGetColorizationColor返回E_INVALIDARG错误

协同 routine 似乎并不比JVM线程占用更少的资源

如何将RESTAssured';S的Http标题转换为<;字符串、字符串和>的映射?

多线程、并发和睡眠未按预期工作

错误:JOIN/ON的参数必须是boolean类型,而不是bigint类型.Java Spring启动应用程序

在JSON上获取反斜杠

java 11上出现DateTimeParseException,但java 8上没有