为什么数据结构不用java_泛谈Java中的不可变数据结构

作为我最近一直在进行的一些编码访谈的一部分,有时会出现不变性问题。我自己并不过分教条,但每当不需要可变状态时,我会试图摆脱导致可变性的代码,这在数据结构中通常是最明显的。然而,似乎对不可变性的概念存在一些误解,开发人员通常认为拥有final引用,或者val在Kotlin或Scala中,足以使对象不可变。这篇博客文章深入研究了不可变引用和不可变数据结构。

不可变数据结构的好处

不可变数据结构具有显着优势,例如:

没有无效的状态

线程安全

易于理解的代码

更容易测试代码

可用于值类型

没有无效的状态

当一个对象是不可变的时,很难让对象处于无效状态。该对象只能通过其构造函数实例化,这将强制对象的有效性。这样,可以强制执行有效状态所需的参数。一个例子:

Address address = new Address();

address.setCity("Sydney");

// address is in invalid state now, since the country hasn't been set.

Address address = new Address("Sydney", "Australia");

// Address is valid and doesn't have setters, so the address object is always valid.

线程安全

由于无法更改对象,因此可以在线程之间共享它,而不会出现竞争条件或数据突变问题。

易于理解的代码

与无效状态的代码示例类似,使用构造函数通常比初始化方法更容易。这是因为构造函数强制执行必需的参数,而setter或initializer方法在编译时不会强制执行。

更易于测试的代码

由于对象更具可预测性,因此不必测试初始化方法的所有排列,即在调用类的构造函数时,该对象有效或无效。使用这些类的代码的其他部分变得更可预测,具有更少的NullPointerException机会。有时,当传递对象时,有些方法可能会改变对象的状态。例如:

public boolean isOverseas(Address address) {

if(address.getCountry().equals("Australia") == false) {

address.setOverseas(true); // address has now been mutated!

return true;

} else {

return false;

}

}

一般来说,上面的代码是不好的做法。它返回一个布尔值,并可能改变对象的状态。这使得代码更难理解和测试。更好的解决方案是从Address 类中删除setter ,并通过测试国家名称返回一个布尔值。更好的方法是将此逻辑移动到 Address 类本身(address.isOverseas())。当确实需要设置状态时,在不改变输入的情况下制作原始对象的副本。

可用于值类型

想象一下金额,比如10美元。10美元将永远是10美元。在代码中,这可能看起来像 public Money(final BigInteger amount, final Currency currency)。正如您在此代码中看到的那样,不可能将10美元的值更改为除此之外的任何值,因此,上述内容可以安全地用于值类型。

最终引用不要使对象不可变

如前所述,我经常遇到的问题之一是这些开发人员中的很大一部分并不完全理解最终引用和不可变对象之间的区别。似乎这些开发人员的共同理解是,变量成为最终的那一刻,数据结构变得不可变。不幸的是,这并不是那么简单,我想一劳永逸地把这种误解带出世界:

A final reference does not make your objects immutable!

换句话说,下面的代码并没有使对象不变:

final Person person = new Person("John");

为什么不?好吧,虽然person是最后一个字段而且无法重新分配,但是 Person类可能有一个setter方法或其他mutator方法,可以执行如下操作:

person.setName("Cindy");

无论最终修饰符如何,这都是一件非常容易的事情。或者, Person类可能会公开这样的地址列表。访问此列表允许您向其添加地址,因此,如下所示改变 person对象:

person.getAddresses().add(new Address("Sydney"));

好了,既然我们已经解决了这个问题,那么让我们深入了解一下我们如何使类不可变。在设计我们的类时,我们需要记住几件事:

不要以可变的方式暴露内部状态

要在内部改变状态

确保子类不会覆盖上述行为

根据以下准则,让我们设计一个更好的Person class 版本 。

public final class Person {// final class, can't be overridden by subclasses

private final String name; // final for safe publication in multithreaded applications

private final List

addresses;

public Person(String name, List

addresses) {

this.name = name;

this.addresses = List.copyOf(addresses); // makes a copy of the list to protect from outside mutations (Java 10+).

// Otherwise, use Collections.unmodifiableList(new ArrayList<>(addresses));

}

public String getName() {

return this.name; // String is immutable, okay to expose

}

public List

getAddresses() {

return addresses; // Address list is immutable

}

}

public final class Address { // final class, can't be overridden by subclasses

private final String city; // only immutable classes

private final String country;

public Address(String city, String country) {

this.city = city;

this.country = country;

}

public String getCity() {

return city;

}

public String getCountry() {

return country;

}

}

现在,可以使用以下代码:

import java.util.List;

final Person person = new Person("John", List.of(new Address(“Sydney”, "Australia"));

现在,上面的代码是不可变的,但是由于Person 和 Address 类的设计 ,同时还有最终引用,因此无法将person变量重新分配给其他任何东西。

更新:正如有些人提到的,上面的代码仍然是可变的,因为我没有在构造函数中复制地址列表。因此,如果不在ArrayList() 构造函数中调用new ,仍然可以执行以下操作:

final List

addresses = new ArrayList<>();

addresses.add(new Address("Sydney", "Australia"));

final Person person = new Person("John", addressList);

addresses.clear();

但是,由于在构造函数中创建了一个新副本,上面的代码将不再影响类中复制的地址列表引用Person ,从而使代码安全。

我希望上述内容有助于理解最终和不变性之间的差异。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

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

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

相关文章

java后台解析json并保存到数据库_[Java教程]ajax 发送json 后台接收 遍历保存进数据库...

[Java教程]ajax 发送json 后台接收 遍历保存进数据库0 2017-09-25 15:00:23前台怎么拿参数的我就不管了我也不会反正用这个ajax没错ajax 代码 一定要写明http请求类型 {contentType:"application/x-www-form-urlencoded; charsetutf-8",}这一段很重要(就是因为这个…

java实时更新权限_java – 如何使用Spring Security重新加载用户更新的权限

如果您需要动态更新登录的用户权限(当这些权限发生变化时,无论什么原因),无需登出并登录,您只需要重置Spring SecurityContextHolder中的Authentication对象(安全令牌)即可.例&#xff1a;Authentication auth SecurityContextHolder.getContext().getAuthentication();List u…

gitlab ci 配置 java_GitLab CI/CD 配置

GitLab CI/CD 配置概念操作示例创建测试项目 sample-web&#xff0c;然后打开项目的 Runners 配置找到这个地方&#xff0c;后边要用然后搭建 gitlab-runner&#xff0c;CI/CD 需要通过它实现# 创建 gitlab-runner 目录mkdir -p /usr/local/gitlab-runner && cd /usr/l…

mysql的dbconn_管道错误使用mysql.conn

我完全迷路了。这是我的管道。我说我跑错了File "c:\python27\lib\site-packages\twisted\internet\defer.py", line 588, in _runCallbackscurrent.result callback(current.result, *args, **kw)File "C:\Python27\bff\bff\pipelines.py", line 42, in …

java失败javac成功_JAVA SE JDK-10安装、配置(解决java成功,javac失败问题)

本文是在win10环境下安装JDK-10在linux环境下安装JDK-10请点击&#xff1a;CentOS 7 安装、配置JDK-101、下载JDK包点击打开下载网站点击图中的Accept License Agreement&#xff0c;再选择Win版本进行下载2、安装JDK包下载完毕&#xff0c;运行安装程序修改安装路径(这里选择的…

java搭建聊天服务器_使用 ServerSocket 建立聊天服务器-2

1.从serverListener中可以看出,每一个客户端创建新的请求之后,都会把它分配给一个独立的chatsocket ,但是每一个ChatSocket都是相互独立的,他们之间并不能沟通,所以要新建一个类,将这些新建的线程管理起来,然后实现他们之间的相互通信.(类似于微信加群聊天的功能)由于一个聊天服…

java实验10流_实验9 Java输入输出流

《实验9 Java输入输出流》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《实验9 Java输入输出流(14页珍藏版)》请在金锄头文库上搜索。1、山 西 大 学 计 算 机 与 信 息 技 术 学 院实 验 报 告姓 名 颜 凯 丽 学 号 2010242100 专业班级 软件工程一班课程名称 Java …

java获取子路径_Java中路径的获取

1.Java中获取路径的方法&#xff1a; Class_Name.class .getResource("") // Class_Name.class .getResource("/") // 获得当前类所在路径 Java中根路径就是binClass_Name.class .getResource("user.dir") // 获得当前类所…

arm9 安装java_QT5.7 AM1808 ARM9的交叉编译

目前&#xff0c;我们正在开发QT&#xff0c;用于在AM1808处理器上运行的硬件的应用程序开发 .目前&#xff0c;我们使用QT创建者开发了我们的应用程序&#xff0c;编译器版本为Qt 4.8.7 .我们计划用Qt 5.7升级它&#xff0c;为此&#xff0c;我们从中下载相应的包我们成功下载…

php pdf 文字水印图片,php如何给pdf加上文字水印和图片水印[未测试]

php给pdf加上水印环境php5.5.12fpdi-1.5.2fpdf-1.7原理利用fpdi来加载已知pdf文件&#xff0c;用fpdf对pdf进行操作注意事项免费的fpdi只支持处理pdf1.4及以下版本&#xff0c;1.5以上就需要用到FPDI PDF-Parser插件使用方法fpdi-1.5.2fpdf-1.71.文字水印 word.phprequire_once…

php调接口post,php模拟post提交请求,调用接口

/*** 模拟post进行url请求* param string $url* param string $param*/function request_post($url , $param ) {if (empty($url) || empty($param)) {return false;}$postUrl $url;$curlPost $param;$ch curl_init();//初始化curlcurl_setopt($ch, CURLOPT_URL,$postUrl)…

php 中class,PHP5中的类(class) (转)

类的结构&#xff1a;类的内部能可能有三种东西&#xff0c;就是常量(constant)&#xff0c;属性(property)和方法(method)&#xff0c;功能可以理解成类外部的常量&#xff0c;变量和函数。在这之中&#xff0c;属性和方法又可以使用public, protected, private三个不同的关键…

Php jsondb,JsonDB-PHP

JsonDB项目介绍JsonDB是一个由原生PHP实现的文件数据库&#xff0c;JsonDB只有一个文件&#xff0c;如果你不想使用庞大的数据库系统&#xff0c;而且只需要单机功能&#xff0c;那么JsonDB就是你最佳的选择。 JsonDB只有初始化写入和查询功能,所以适用于比较固定的,数据量不大…

php 发送post请求json,thinkphp ,php post发送json请求,就收post请求

发送方的代码如下namespace Api\Controller;use Think\Controller;class IndexController extends Controller{public function send(){ini_set("error_reporting","E_ALL & ~E_NOTICE");$obj->phonenum 15340958922;$data json_encode($obj);$ur…

lavarel php区别,laravel中{{}}和{!! !!}的区别详解

本篇文章介绍了laravel中{{}}和{!! !!}的区别&#xff0c;感兴趣的朋友可以参考下。laravel中{{}}和{!! !!}的区别详解1.{{}}和{!! !!} 中{{}}支持转义 一段html代码只是被当成普通的字符串输出 &#xff0c;{!! !!} 不支持转义 一段html代码可以被正常的解析1.2具体什么意思呢…

php获取ios,IOS 通过描述获取UDID PHP代码版

难点在于PHP对XML的解析文件结构&#xff1a;第一步&#xff1a;新建HTML文件 用于跳转安装描述文件 必须在safari里面打开地址第二步&#xff1a;新建udid.mobileconfig文件PayloadContentURLhttps://www.xxx.com/pay_super/receive.phpDeviceAttributesUDIDIMEIICCIDVERSIONP…

matlab如何读取csv,Matlab:如何读取CSV文件以及如何读取带有字符串数据项的CSV文件 | 学步园...

CSV&#xff0c;逗号分开的文件&#xff0c;如果能快速的读取这些文件中的数据&#xff0c;无疑会帮助我们解决很多问题。1、 只有数据的CSV文件&#xff0c;CSV file that includes only numbers.As an example, create a text file, named as data.csv if you prefer, which …

matlab怎么写集合,matlab集合操作

C intersect(A,B) -- 返回A和B的交集(相同元素)%----C中的元素将会从小到大排序----%如果A和B都是向量&#xff0c;那么返回A、B中的相同元素如果A和B都是矩阵&#xff0c;intersect(A,B,rows) 将返回A、B中的相同列还有一种调用格式&#xff1a;[C,IA,IB] intersect(A,B);其…

php表单验证内容不能为空,php校验表单检测字段是否为空的方法_PHP教程

php校验表单检测字段是否为空的方法这篇文章主要介绍了php校验表单检测字段是否为空的方法,涉及php验证表单的技巧,非常具有实用价值,需要的朋友可以参考下本文实例讲述了php校验表单检测字段是否为空的方法。分享给大家供大家参考。具体如下&#xff1a;php校验表单&#xff0…

url中隐藏php后缀,url中如何隐藏.php

url中隐藏“.php”的方法&#xff1a;首先找到并打开“nginx.conf”配置文件&#xff1b;然后添加内容“location / {ttry_files $uri $uri/ $uri.php$is_args$args;}”&#xff1b;最后保存修改即可。推荐&#xff1a;《PHP视频教程》现在很多人都喜欢用nginx作为Web服务器部署…