java属性定义public有风险么_【java】Java 代码不写getter, setter, 转化为 public, 有什么弊端?...

2017/1/5

描述

一个字段被认为是私有的, 那么外部就不能访问它( 最多提供getter )

如果一个私有的字段提供了getter和setter, 那么它就被认为是公开的, 那就前后矛盾了, 应该使用 public

目的是什么

1. 一直不重视这个问题的「why」, 而只关注 how, 导致东西理解不透彻

2. anyway, 我甚至想弄清楚 what, 以刷新三观

相关代码

public class Bicycle {

private int cadence;

private int gear;

// 比如speed被认为是公开的

// 所以改成 public 的

// 然后移除对应的 getter setter

private int speed;

public Bicycle(int startCadence, int startSpeed, int startGear) {

gear = startGear;

cadence = startCadence;

speed = startSpeed;

}

public int getCadence() {

return cadence;

}

public void setCadence(int newValue) {

cadence = newValue;

}

public int getGear() {

return gear;

}

public void setGear(int newValue) {

gear = newValue;

}

public int getSpeed() {

return speed;

}

public void applyBrake(int decrement) {

speed -= decrement;

}

public void speedUp(int increment) {

speed += increment;

}

}

上下文环境

产品版本: Java 8

尝试解决

我自己的理解

如果定义为 public 的, 那么日后要改这个字段就不容易, 而函数的「可修改性」就很强 ( 姑且称 method 为函数 )

但是真的要为以后想那么多吗( 代码是需要不断重构的, 预先空想可能是 over-design )

update:

一个python的例子, 怎么说呢, 总感觉python的这种写法舒服点( 虽然定义的时候, 代码没有比java少多少, 但是调用的时候舒服点 )

In [1]: class MyClass(object):

...: def __init__(self, name, age, gender):

...: self._name = name

...: self._age = age

...: self._gender = gender

...: @property

...: def name(self):

...: return self._name

...: @name.setter

...: def name(self, value):

...: self._name = value.strip()

...:

In [2]: c = MyClass('hezhiming', 22, 'male')

In [3]: c

Out[3]: <__main__.myclass at>

In [4]: c.name

Out[4]: 'hezhiming'

In [5]: c.name = ' hezhiming '

In [6]: c.name

Out[6]: 'hezhiming'

回答

呃,通过方法去操作其实有很多事情可以干,尤其在配合了OO思想的情况下——类型是一个重要信息。

另外,这是一种标准,Java Bean就是这么一回事,许多框架、第三方库(最著名的就是Spring)的“正常运作”也依赖于此。你可以搜索一下“Java 帝国之Java bean”相关的文章。

强烈反对关于Setter写逻辑之类的答案。这样的代码极具误导性,有经验的人都知道不可以把逻辑写到"bean"层(一般对应Model层),起码要写到Service层(按照常见的三层式架构)。故此,这个情况下的Setter并不是显得那么重要了。综上,答的关于Setter里写逻辑的,都是不正确的。

最后评价一下那个说存在即合理的人,希望他能去好好理解一下“存在即合理”究竟是什么意思。

正式回应一下踩我的那些人————其中就不乏泛着说Setter里写逻辑的人,也有靠着“歪理邪说”来说服别人的人。

说个故事吧,我一年前刚来社区的时候。曾看见过一个分数比较高的大神签名版上写着垃圾SF!,我注意到了他的声望记录(那时声望记录和GitHub的提交记录有点相似,不过不是记录1年这么长),几乎所有问题都被踩了一遍,很显然——这是恶意的。

在混迹社区一年多以来,我偶尔也会碰到这样的情况。不过手段并没有这么恶劣,同样,我承认我还是很菜的,对于有些问题吃不准,指不定就是误人子弟,挨踩就挨踩吧!

而今这个情况,是令我无法忍受的——错了就是错了,还要去误导别人。如果别人面试因为这种如此初级的题失利而被筛掉,那是多么的可惜啊!

最后,我想告诉 all:

我不会因为被踩了而退出社区,哪怕是恶意踩。因为我还要去回答更多的问题,防止更多的新手被“可怕的初学者”误导。同样,为了维护社区的质量,我对极具误导性的回答也决不会放过。

如果不信我的话,请在面试的时候告诉面试官你经常在Java Bean的Setter里写逻辑吧!

刚才我仔细的扫了一遍全部的答案,幸好有@kevinz 这样的用户在,我为他顶了一票,防止“可怕的初学者”将他摁下去。

回答+1分,跟风回答也才就1分。被踩就是2分。而且这样的答案越来越多,社区的质量就越来越差,届时,就算你有1W的声望,在你聊天 or 交流的时候骄傲的说了出来,别人也只会嗤之以鼻:那个社区的质量并不高,都是灌水跟风的。

每种语言的哲学不一样。Java 讲的是完全的面向对象,在编码时提倡代码高度的灵活性与可扩展性。而像Go语言提倡的又是代码极致的简洁性,所以只需要像下面的方式定义struct就ok。

type S struct {

A string

B int

}

但是在Java中你还是应该遵守规范为javabean定义get/set方法,因为你遵守规范才来享受规范为你带来的好处,比如你接入第3方库的时候,要使用reflect的方式来操作javabean时他们大多数都是采用get/set方法来实现的。如果你的javabean此时没有get/set方法那显然你是无法使用该库的。

以个人经验感受,代码的简洁性比代码的灵活性与可扩展性要重要。所以java中也出现了像lombok这种工具包可以很好的简化代码。

上面的人说了这么多,都说漏了一点,方法具有多态性,属性没有多态性,此话怎么理解?比如Person为父类,Man为子类,父类和子类都有Name属性(虽然这很罕见),父类和子类中的name都为public。请看如下代码:

Person p = new Man();

p.name = "person";

Print(p.name);

Print(p.getName());

答案会输出

person

null

这个问题问得好!为什么我们不直接把这个私有字段变成public,而要用麻烦的setter和getter去做呢?原因有几个:

永远不要相信客户的输入

我们来假设这个类:

class Bicycle {

public int speed;

// ...

}

public class BicycleTest{

public static void main(String[] args){

Bicycle b = new Bicycle();

b.speed = 300000; //什么?自行车这么快?不科学啊?自行车车速不超过120吧。

// 可是作为程序员的我怎么办呢?只能在文档里告诉用这个类的人:超过120罚款?

// WTF!写这个类的程序员该死。

b.speed = -30; // 这个?负的速度,高中物理?我勒个去。

}

}

我们如果改成这个,是不是好点:

class Bicycle {

private int speed;

public int setSpeed(int speed) {

if(speed > 120){

this.speed = 120;

} else if(speed < 0){

this.speed = 0;

} else{

this.speed = speed;

}

}

public int getSpeed() {

return speed;

}

}

public class BicycleTest{

public static void main(String[] args){

Bicycle b = new Bicycle();

b.speed = 300000;

System.out.println(b.getSpeed()); // 显示120

}

}

便于修改

假如说,现在要求speed是0-40之间。如果没有getter和setter,所有的代码要重写一下,没有1个钟头时间基本不可能完成。有了这个setter的话,简单,改一下setter的逻辑,1分钟不完工,这程序员可以开了。

public int setSpeed(int speed) {

if(speed > 40){

this.speed = 40;

} else if(speed < 0){

this.speed = 0;

} else{

this.speed = speed;

}

}

我的理解是利弊是相对的,对于一个“小程序”来说,没有什么弊端,因为你和其它维护的人能清晰看到和把控参数值的变化,这种时候,因为你能完全把控程序的每个角落,public甚至“利大于弊”,因为没有了setter&getter,减少了不少代码量。但当程序“大“到一定规模程度的时候,是不是应该要考虑程序的可维护性呢,比如a.money和a.getMoney(),突然有一天加入了一个a.action因子来影响money,如何保证每一个money的调用者都能知道因子影响规则,显然直接a.money就不那么可靠了。从写程序的角度来说,应该多写方法,这是我大学计算机程序课老师教我的,数据很枯燥,唯方法能让你形象地知道程序在干什么。从面向对象的角度来说,对象应该提供操作对象的方法,所以,还是方法,setter和getter就是体现了这一思想。从Java特性来说,setter和getter体现了封装特性的思想,就上面的例子,当另外一个money使用者需要调用money时,调用者本不需要知道还有一个action因子在那里,他只要getMoney()拿到正确的money就可以了,变量私有,而提供操作变量的公共方法。这时,利大于弊。才疏学浅,一家之言,理解不妥之处接受批评指正。

补充,对于setter&getter内逻辑问题,我觉得是一个”应不应该的问题“,而不是一个”能不能的问题“,对于后者,我会说能,为什么不能!而对于前者,得权衡利弊,当你程序逻辑、风险把控很好时,能方便到你,写啊!那些优秀的框架不那么写,肯定也是利弊考量(扩展等),逻辑放在逻辑处理层,也是正常啊。

首先method的翻译是方法。

然后我来理解一下你的困惑是

但是真的要为以后想那么多吗

而其实你不这么做的原因主要还是因为这样做很麻烦吧。

但是实际上,你会拥有那些必须使用getter setter的域(字段,属性),比如只读的域,所以1⃣️,为了整体代码的一致性,还是全部getter setter的好。

2⃣️,使用ide的帮助其实并不用花太多功夫就可以完成。

3⃣️,这基本上算是一个规范性的东西,完全不必要思考需不需要这样做。

4⃣️,有些情况的确可以不需要getter setter的,如果你有自信这里的数据都很简单就直接public。

关于另外一个问题

如果一个私有的字段提供了getter和setter, 那么它就被认为是公开的, 那就前后矛盾了, 应该使用 public

目的是什么

首先一个私有字段可能只有getter和setter的一个,这自然算不上公开。如果setter getter都有,也不是你想象的那么简单。因为你自己写的getter setter没任何处理逻辑,但你考虑这个

private String name;

public void setName(String name) {

this.name = name.trim();

}

public String getName() {

return name;

}

如果你有像这个例子一样的需求呢,在存姓名的时候需要先去除掉多余的空格。(顺便我不敢保证上面代码语法没问题,好久没写java了。

关于开放字段调用会更舒服的问题,其实题主你提到的python的开放字段,并不是开放字段对不对。实际存储字段是_name,而getter和setter是name,这和java仅仅是语法的不同,本质是一样的。

我再放两段代码来展示一下getter和setter的好处。

private int userId;

public User getUser();

public void setUser(user);

再比如

private Map data;

public User getUser();

public int getStatus();

class GoodDog {

private int size;

public int getSize() {

return size;

}

public void setSize(int s){

size = s;

}

}

这里就有一个封装和控制的问题。假设你直接访问属性 goodDog .size;突然有一天,你可能需要对每个尺寸,或者某个尺寸过滤掉一些东西,怎么办?那么你就只好在出现goodDog .size的地方到处添加过滤机制。如果你使用getSize()方法,那么我就在这个方法里面过滤一下就OK了。 其实总体思想就是一个面向对象的观点来做事情,你要什么,就给我讲,我到屋里给你拿出来,但是你却不可以直接进屋去拿,万一你不熟悉我家里的情况,把我家搞乱了咋个办啊。

如果goodDog .size=12这样直接设置的话不安全,万一size的值是小于10的话

是不是在每个属性设置的地方都要进行验证,为了不让这种事情发生,Java就统一的在set方法的时候改变值,控制范围,这样就不要以后需求变化的时候满世界的找那些地方使用了goodDog .size

有参方法和无参方法要看方法的具体用途 set方法和get方法是为了体现面向对象编程的封装思想 把成员变量设为private 只能通过特定方法修改和访问 保证了程序的安全性.

还有上面的回答,像java bean或者hibernate这些,它去属性不是取你定义的属性size,而是取你的getSize,然后去掉get方法S小写,得到size。

再或者还是不理解就记住一点:存在即合理,总有一天,在写某一段代码的时候,你会恍然大悟。

简单场景可以使用public,setter和getter主要是对外界封闭作用,可以在setter和getter里面加一些统一的处理,重构也方便。

我补充一点,隐藏可见性,setter和getter方法不一定都是直接存取值,也可以加入一些处理逻辑。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值