简体   繁体   English

为什么最终关键字对于不可变类是必要的?

[英]Why final keyword is necessary for immutable class?

Could you please clarify that why final keyword is required before class when we are making it an immutable one. 您能否澄清一下,为什么在课堂上我们将最终关键字作为不可变关键字时需要它。 I mean, if we declare all of it's attributes as private and final, then also it is an immutable class, isn't it? 我的意思是,如果我们将所有属性声明为private和final,那么它也是一个不可变类,不是吗?

Sorry if the question seems easy, but i am truly confused about it. 对不起,如果问题看起来很容易,但我真的很困惑。 Help me out. 帮帮我。

Editted: I know that a class declared final can't be subclassed.. But if each attribute is private and final then what difference does that make? Editted:我知道一个声明final的类不能被子类化。但是如果每个属性都是私有的,那么最终会有什么区别呢?

As stacker says, final makes sure the class isn't subclassed. 正如堆栈器所说, final确保该类没有子类。 That's important so that any code which is relying on its immutability can do so safely. 这一点非常重要,因此任何依赖其不变性的代码都可以安全地完成。

For example, immutable types (where each field is also of an immutable type) can be freely used between threads without worrying about data races etc. Now consider: 例如,不可变类型(其中每个字段也是不可变类型)可以在线程之间自由使用,而不必担心数据争用等。现在考虑:

public class Person {
    private final String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

That looks like you can share Person instances freely across threads with no problem. 看起来你可以在线程之间自由地共享Person实例而没有任何问题。 But what about when the object you're sharing is actually a mutable subclass: 但是,当您共享的对象实际上是一个可变的子类时:

public class Employee extends Person {
    private String company;

    public Employee(String name, String company) {
        super(name);
        this.company = company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getCompany() {
        return company; 
    }
}

Now instances of Employee aren't safe to share between threads, because they're not immutable. 现在, Employee实例在线程之间共享不安全的,因为它们不是不可变的。 But the code doing the sharing may only know about them as instances of Person ... leading them into a false sense of security. 但是进行共享的代码可能只知道它们是Person实例......导致它们陷入虚假的安全感。

The same goes for caching - it should be safe to cache and reuse immutable types, right? 缓存也是如此 - 缓存和重用不可变类型应该是安全的,对吧? Well, it is safe to cache instances which are genuinely of an immutable type - but if you're dealing with a type which itself doesn't allow mutation, but does allow subclasses, it's suddenly not safe any more. 那么,它安全的缓存实例那些真正不可变型的-但如果你正在处理一个类型本身不允许突变, 允许子类,它是突然不是安全了。

Think about java.lang.Object . 想想java.lang.Object It doesn't have any mutable fields, but it's clearly a bad idea to treat every Object reference as if it's a reference to an immutable type. 它没有任何可变字段,但将每个Object引用视为对不可变类型的引用显然是个坏主意。 Basically it depends on whether you think about immutability as a property of the type or of objects. 基本上,这取决于您是否将不可变性视为类型或对象的属性。 A truly immutable type declares "any time you see a reference of this type, you can treat it as immutable" - whereas a type which allows arbitrary subclassing can't make that claim. 一个真正不可变的类型声明“任何时候你看到这种类型的引用,你可以将它视为不可变” - 而允许任意子类化的类型不能提出这种说法。

As an aside, there's a half-way house: if you can limit the subclassing to only "trusted" places, you can ensure that everything's immutable, but still allow that subclassing. 顺便说一句,有一个中途的房子:如果你可以将子类限制为只有“受信任”的地方,你可以确保一切都是不可变的,但仍然允许子类化。 The access in Java makes that tricky, but in C# for example you could have a public class which only allowed subclassing within the same assembly - giving a public API which is nice and strong in terms of immutability, while still allowing for the benefits of polymorphism. Java中的访问使得这很棘手,但是在C#中你可以拥有一个只允许在同一个程序集中进行子类化的公共类 - 提供一个在不变性方面很好而且强大的公共API,同时仍然允许多态的好处。

A class that is declared final cannot be subclassed. 声明为final的类不能被子类化。 See also http://docs.oracle.com/javase/tutorial/java/IandI/final.html 另见http://docs.oracle.com/javase/tutorial/java/IandI/final.html

The different semantics of all uses of the final keyword are described in the The Java Language Specification Java语言规范”中描述了final关键字的所有用法的不同语义

  • 4.12.4 final Variables Page 80 4.12.4最终变量第80页
  • 8.1.1.2 final Classes Page 184 8.1.1.2最终类Page 184
  • 8.3.1.2 final Fields Page 209 8.3.1.2 final Fields Page 209
  • 8.4.3.3 final Methods Page 223 8.4.3.3最终方法页面223

'final' as the keyword's name suggest means that the attribute to which final keyword is attached can't be changed(in terms of value) in other words it behaves like a constant. 'final'作为关键字的名称建议意味着最终关键字所附加的属性不能更改(就值而言),换句话说,它的行为类似于常量。

As per your question if all members of the class is made private and final but the class is not made final then the same class can be inherited but the super class member are immutable as final keyword is attached to them. 根据你的问题,如果类的所有成员都是私有的和最终的,但是类不是最终的,那么同一个类可以继承,但是超类成员是不可变的,因为最终的关键字被附加到它们。

You don't strictly need final to make an immutable class. 你并不需要 final来制作一个不可变的类。 ie you can make an immutable class without it being final. 也就是说,你可以创建一个不可变的类而不是最终的。

However, if you don't make it final, then it is possible for someone to extend a class and create a subclass that is mutable (either by adding new mutable fields, or overriding methods in a way that enables you to mutate protected fields of the original immutable class). 但是,如果你没有使它成为最终的,那么有人可以扩展一个类并创建一个可变的子类(通过添加新的可变字段,或以一种允许你改变受保护字段的方式重写方法)原始的不可变类)。 This is a potential problem - it violates the Liskov Substitution Principle , in the sense that you would expect the property of immutablity to be preserved by all subtypes. 这是一个潜在的问题 - 它违反了Liskov替换原则 ,在某种意义上,你会期望所有子类型都保留不可变性的属性。

Hence, it is usually good practice to make immutable classes final to avoid this risk. 因此,通常最好将不可变类最终化以避免这种风险。

An immutable object is an object which state is guaranteed to stay identical over its entire lifetime. 不可变对象是一个对象,该状态保证在其整个生命周期内保持相同。 While it is perfectly possible to implement immutability without final, its use makes that purpose explicit, to the human (the software developer) and the machine (the compiler). 虽然完全可以在没有最终版本的情况下实现不变性,但是它的使用对于人类(软件开发者)和机器(编译器)来说是明确的。

Immutable objects carry some very desirable characteristics: 不可变对象具有一些非常理想的特征:

they are simple to understand and easy to use
they are inherently thread-safe: they require no synchronization
they make great building blocks for other objects 

Clearly final is going to help us define immutable objects. 显然,final将帮助我们定义不可变对象。 First in labelling our object as immutable, which makes it simple to use and understand by other programmers. 首先将我们的对象标记为不可变,这使得其他程序员易于使用和理解。 Second in guaranteeing that the object's state never changes, which enable the thread-safe property: thread concurrency issues are relevant when one thread can change data while another thread is reading the same data. 第二,保证对象的状态永远不会改变,从而启用线程安全属性:当一个线程可以更改数据而另一个线程正在读取相同的数据时,线程并发问题是相关的。 Because an immutable object never changes its data, synchronizing access to it is not needed. 因为不可变对象永远不会更改其数据,所以不需要同步对它的访问。

Create an immutable class by meeting all of the following conditions: 通过满足以下所有条件创建不可变类:

Declare all fields private final.
Set all fields in the constructor.
Don't provide any methods that modify the state of the object; provide only getter methods (no setters).
Declare the class final, so that no methods may be overridden.
Ensure exclusive access to any mutable components, e.g. by returning copies.

A class declared final cannot be sub classed. 宣布为final的类不能被分类。 Other classes cannot extend final class. 其他课程不能延长最终课程。 It provides some benefit to security and thread safety. 它为安全性和线程安全性提供了一些好处。

If all public and protected methods are final and none of them allows modifying private fields, and all public and protected fields are both final and immutable, then I guess it could be said class is semi-immutable, or sort of constant. 如果所有公共和受保护的方法都是最终的,并且它们都不允许修改私有字段,并且所有公共和受保护的字段都是最终的和不可变的,那么我想可以说类是半不可变的,或者是常量。

But things break down when you create a subclass and need to override equals and hashcode. 但是当你创建一个子类并且需要重写equals和hashcode时,事情就会崩溃。 And can not because you made them final... So the whole thing is broken, so just make the whole class final to prevent programmer from being a fool by accident. 并且不能因为你让它们成为最终的...所以整个事情都被打破了,所以只要让整个班级最终成功,以防止程序员偶然成为一个傻瓜。

As an alternative to doing this kind of bastardized version immutability, you have several options. 作为这种混合版本不变性的替代方法,您有几种选择。

If you want to attach extra data to immutable instance, use Map . 如果要将额外数据附加到不可变实例,请使用Map Like if you wanted to add age to name, you would not do class NameAge extends String ... :-) 就像你想把年龄添加到名字一样,你不会做class NameAge extends String ... :-)

If you want to add methods, create a class of static utility functions. 如果要添加方法,请创建一组静态实用程序函数。 That is a bit klunky, but it is the current Java way, Apache commons for example is full of such classes. 这有点笨拙,但它是当前的Java方式,例如Apache公共充满了这样的类。

If you want to add extra methods and data, create a wrapper class with delegate methods to methods of the immutable class. 如果要添加额外的方法和数据,请使用委托方法创建一个包装类,以使用不可变类的方法。 Anybody needing to use the extra methods needs to be aware of them anyway, and there is not much practical difference in casting to derived non-immutable class or doing something like new MyWrapper(myImmutableObj) for many use cases. 无论如何,任何需要使用额外方法的人都需要了解它们,并且对于许多用例,在转换为派生的非不可变类或执行类似new MyWrapper(myImmutableObj)类的内容方面没有太大的实际区别。

When you really have to have reference to original imutable object (like storing it in existing class you can not change), but need the extra data somewhere, you need to use the Map approach to keep the extra data around, or something like that. 当你真的必须引用原始的可模拟对象(比如将它存储在现有的类中,你无法改变),但需要在某处需要额外的数据时,你需要使用Map方法来保存额外的数据,或类似的东西。

If an immutable class Foo is sealed ("final"), then anyone who receives a reference to a Foo may be assured that if Foo was implemented correctly, the referenced instance will in fact be immutable. 如果一个不可变类Foo被密封(“final”),那么接收对Foo的引用的任何人都可以确信如果Foo被正确实现,那么引用的实例实际上将是不可变的。 If an immutable class is not sealed, then someone who receives a reference to a Foo may be assured that if the actual class of of the referenced object (which may be Foo or some derivative type implemented by some arbitrary unknown person ) was implemented correctly, the instance will be immutable. 如果一个不可变类没有被密封,那么接收对Foo的引用的人可以确信,如果被引用对象的实际类(可能是Foo某些任意未知的人实现的某种衍生类型)被正确实现,实例将是不可变的。 Leaving Foo unsealed means that anyone who relies upon Foo to be immutable will have to trust that everyone who writes a class that derives from Foo will implement it correctly. 离开Foo意味着任何依赖Foo人都是不可变的,必须相信每个编写一个派生自Foo的类的人都会正确地实现它。 If one wants to be certain that every reference to a Foo will in fact target an immutable instance without having to rely upon the authors of derivative classes to abide by the contract, making Foo final can aid in such assurance. 如果想要确定每个对Foo引用实际上都是针对不可变实例而不必依赖衍生类的作者来遵守合同,那么使Foo最终可以帮助这样的保证。

On the other hand, the possibility that a class might derive from Foo but violate its immutability isn't terribly different from the possibility that a class which derives from any other class might violate the contracts of its parent class. 另一方面,类可能派生于Foo但违反其不变性的可能性与源自任何其他类的类可能违反其父类的合同的可能性并没有太大差别。 Any code which accepts a reference of any type which can be subclasssed by outside code might be given an instance of a subclass which violates its parent's contract. 任何接受可以由外部代码子类化的任何类型的引用的代码都可能被赋予违反其父代合同的子类的实例。

The fundamental question when deciding whether an immutable class should be sealed is the same as for any other class: whether the benefits of leaving the type unsealed outweigh any dangers that would be posed by doing so. 决定是否应密封不可变类别时的基本问题与任何其他类别相同:离开未密封类型的好处是否超过了这样做会带来的任何危险。 In some cases, it may make sense to have an extensible immutable class, or even an abstract class or interface whose concrete implementations are all contractually obligated to be immutable; 在某些情况下,拥有一个可扩展的不可变类,甚至是一个抽象的类或接口,其具体实现都是合约义务是不可变的,这是有意义的; for example, a drawing package might have an ImmutableShape class with some concrete fields, properties, and methods to define 2D transformations, but an abstract Draw method, allowing for the definition of derivative types ImmutablePolygon , ImmutableTextObject , ImmutableBezierCurve , etc. If someone implements an ImmutableGradientFilledEllipse class but fails to have that type make its own copy of a mutable GradientColorSelector , the colors of gradient-filled polygons might change unexpectedly, but that would be a fault of the ImmutableGradientFilledEllipse class, and not the consuming code. 例如,绘图包可能有一个ImmutableShape类,其中包含一些用于定义2D转换的具体字段,属性和方法,但是一个抽象的Draw方法,允许定义衍生类型ImmutablePolygonImmutableTextObjectImmutableBezierCurve等。如果有人实现了ImmutableGradientFilledEllipse类但无法使该类型创建自己的可变GradientColorSelector副本,渐变填充多边形的颜色可能会意外更改,但这将是ImmutableGradientFilledEllipse类的错误,而不是消耗代码。 Despite the possibility of a broken implementation failing to uphold the "immutability" contract, an extensible ImmutableShape class would be much more versatile than a sealed one. 尽管实施失败的可能性未能维持“不变性”合同,但可扩展的ImmutableShape密封的类更加通用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM