我对为什么语言有这些感到有点困惑。我是一名 Java 程序员,在我职业生涯的开始阶段,所以 Java 是我写的唯一语言,因为我开始真正了解它。
所以在 Java 中我们当然没有属性,我们编写 getThis() 和 setThat(...) 方法。
拥有属性我们会得到什么?
谢谢。
编辑:另一个查询:在具有属性的语言中会出现哪些命名约定?
我对为什么语言有这些感到有点困惑。我是一名 Java 程序员,在我职业生涯的开始阶段,所以 Java 是我写的唯一语言,因为我开始真正了解它。
所以在 Java 中我们当然没有属性,我们编写 getThis() 和 setThat(...) 方法。
拥有属性我们会得到什么?
谢谢。
编辑:另一个查询:在具有属性的语言中会出现哪些命名约定?
你觉得哪一个更自然?
// A
person.setAge(25)
// B
person.age = 25;
// or
person.Age = 25; //depending on conventions, but that's beside the point
大多数人会回答B。
它不仅是语法糖,在进行反射时也有帮助;您实际上可以在数据和操作之间做出区别,而无需借助方法的名称。
对于不熟悉属性的人,这里有一个 C# 示例:
class Person
{
public int Age
{
set
{
if(value<0)
throw new ArgumentOutOfRangeException();
OnChanged();
age = value;
}
get { return age; }
}
private int age;
protected virtual void OnChanged() { // ... }
}
此外,大多数人总是使用属性而不是稍后提升公共成员,原因与我们总是使用 get/set 的原因相同;无需重写绑定到数据成员的旧客户端代码。
语法更好:
button.Location += delta;
比:
button.setLocation(button.getLocation() + delta);
编辑:
下面的代码假定您是手动完成所有操作。在我的示例世界中,编译器将生成简单的 get/set 方法并将所有直接变量访问转换为这些方法。如果不是这样,则必须重新编译客户端代码,这在很大程度上违背了目的。
原来的:
属性的主要论点是,如果您从变量转到方法,则无需重新编译代码。
例如:
public class Foo
{
public int bar;
}
如果我们后来决定对“bar”进行验证,我们需要这样做:
public class Foo
{
private int bar;
public void setBar(final int val)
{
if(val <= 0)
{
throw new IllegalArgumentException("val must be > 0, was: " + val);
}
bar = val;
}
public int getBar()
{
return (bar);
}
}
但是添加 set/get 方法会破坏所有代码。如果它是通过属性完成的,那么您将能够在事后添加验证而不会破坏客户端代码。
我个人不喜欢这个想法 - 我对使用注释和自动简单的 set/geterate 的想法更满意,并且能够根据需要提供自己的 set/get 实现(但我不喜欢隐藏的方法来电)。
两个原因:
在 Java 中,getter 和 setter 本质上是属性。
在其他现代语言 (c#) 等中,它只是使语法更易于使用/理解。
它们是不必要的,并且在大多数情况下都有解决方法。
这确实是一个偏好问题,但是如果您使用的语言支持它们,我建议您使用它们:)
起初我也为此苦苦挣扎,但是我真的很欣赏它们。在我看来,属性允许我以自然的方式与公开的数据进行交互,而不会丢失 getter/setter 方法提供的封装。换句话说,我可以将我的属性视为字段,但如果我选择不公开实际字段,则不会真正公开。使用 C# 3.0 中的自动属性,对于大多数字段来说,它变得更好——我希望允许消费者读/写数据——我要写的更少:
public string Prop { get; set; }
在我想要部分可见性的情况下,我可以轻松地限制我想要的访问器。
public string Prop { get; private set; }
所有这些都可以通过 getter/setter 方法来完成,但措辞要高得多,用法也不太自然。
面向对象编程的一般规则是永远不要更改现有接口。这确保了虽然内部内容可能会更改,但调用对象的对象不需要知道这一点。
其他语言中的属性是伪装成特定语言特征的方法。在 Java 中,属性仅通过约定进行区分。虽然通常这有效,但在某些情况下它会限制您。例如,有时您会使用 hasSomething 而不是 getSomething 的 isSomething。
因此它允许名称的灵活性,而依赖的工具和其他代码仍然可以区分。
此外,代码可以更紧凑,get 和 set 按设计组合在一起。
在面向对象的软件构造 2中,Bertrand Meyer 将此称为“统一访问原则”,一般的想法是,当一个属性从一个简单的(即只是一个整数)变为派生的(一个函数调用)时,使用它的人不应该知道。
您不希望每个使用您的代码的人都必须从
诠释 x = foo.y;
至
int x = foo.y();
这打破了封装,因为你没有改变你的“接口”只是你的“实现”。
您还可以创建派生字段和只读/只写字段。我在使用过的语言中看到的大多数属性不仅允许您分配简单的字段,还允许您为属性分配完整的功能。
属性提供了一种简单的方法,可以将对象中一组逻辑背后的细节抽象为外部世界的单个值。
虽然您的属性可能仅作为一个值开始,但此抽象将接口解耦,以便以后可以更改其细节而影响最小。
一般的经验法则是抽象和松耦合是好事。属性是实现两者的模式。
语言级别的属性是个坏主意。他们没有很好的约定,他们在代码中隐藏了性能缺陷。
一切都与绑定有关
曾经有一段时间我认为属性只是语法糖(即通过减少它们的类型来帮助开发人员)。随着我进行了越来越多的 GUI 开发并开始使用绑定框架(JGoodies、JSR295),我发现语言级别的属性远不止语法糖。
在绑定场景中,您基本上定义了“对象 A 的属性 X 应始终等于对象 B 的属性 Y”的规则。简写是:Ax <-> By
现在,想象一下您将如何实际使用 Java 编写绑定库。现在,绝对不可能将“x”或“y”直接称为语言原语。您只能将它们称为字符串(并通过反射访问它们)。本质上,A."x" <-> B."y"
当你去重构代码时,这会导致大量的问题。
还有其他注意事项,包括正确实施属性更改通知。如果你看一下我的代码,每个有福的 setter 至少需要 3 行代码来完成非常简单的事情。再加上这 3 行之一包括另一个字符串:
public void setFoo(Foo foo){
Foo old = getFoo();
this.foo = foo;
changeSupport.firePropertyChange("foo", old, foo);
}
所有这些四处飘荡的弦乐都是一场彻头彻尾的噩梦。
现在,想象一下,如果财产是该语言的一等公民。这开始提供几乎无限的可能性(例如,想象直接使用 Property 注册侦听器,而不是必须使用 PropertyChangeSupport,这是必须添加到每个类的 3 个神秘方法)。想象一下能够将属性本身(不是属性的值,而是属性对象)传递到绑定框架中。
对于 Web 层开发人员,想象一个 Web 框架,它可以根据属性本身的名称(例如 registerFormProperties(myObject.firstname, myObject.lastname, someOtherObject.amount) 来构建它自己的表单 id 值,以允许对象的往返填充表单提交回服务器时的属性值。现在要做到这一点,你必须传入字符串,重构变得很头疼(一旦你依赖字符串和反射来连接东西,重构实际上变得非常可怕)。
所以无论如何,对于我们这些通过绑定处理动态数据更新的人来说,属性是语言中非常需要的特性——不仅仅是语法糖。