java 子类化_如何在Java中安全地进行子类型化
你可能还记得,Liskov Substitution原则是关于承诺和合同的。但究竟是什么承诺?这是关于保证子类型的安全性。这意味着子类型必须保证有人可以从超类型中合理地推断出来。它必须具有传递关系。在数学中,我们说a a,b,c∈X,如果是aRb和bRc,那么aRc。在面向对象的编程中,子类化(现在,在本文中关于子类化,我指的是子类型)意味着子类型,但不是正确的方式。我们必须确保我们不会破坏我们继承的超类承诺,并且我们不依赖于可能因我们无法控制而改变的东西。如果它发生变化,其他对象可能会受到影响(因为它们是不可变的)。实际上,子类化甚至可能是项目中的错误源。
为什么安全子类型
实际上,子类化实际上是一种特殊类型的子类型,它允许子类型重用超类型实现(目的是防止在超类型中进行小修改的所有内容的重新实现)。我们可以说子类化是子类型,但不是子类型是子类化。子类化带来两件事:子类型(多态)和代码重用。子类型具有最高的冲击力; 超类的公共级别和受保护级别的任何更改都将影响其子类。子类型不是Is-A关系,有时它是Is-A关系。实际上,子类型是代码重用的过程技术和动态多态的工具。
子类化仅涉及实现的内容和方式 - 而不是承诺的内容。如果你违反了基类的承诺,会发生什么?有没有保证可以确保它兼容?即使您的编译器也不会理解这个错误,您将面临代码中的错误,例如:
class DoubleEndedQueue {
void insertFront(Node node) {
// ...
// insert node infornt of queue
}
void insertEnd(Node node) {
// ...
// insert a node at the end of queue
}
void deleteFront(Node node) {
// ...
// delete the node infront of queue
}
void deleteEnd(Node node) {
// ...
// delete the node at the end of queue
}
}
class Stack extends DoubleEndedQueue {
// ...
}
如果类Stack想要以代码重用为目的使用Subtyping,它可能会继承违反其主体的行为,例如 insertFront。我们还看到另一个代码示例:
public class DataHashSet extends HashSet {
private int addCount = 0;
public function DataHashSet(Collection collection) {
super(collection);
}
public function DataHashSet(int initCapacity, float loadFactor) {
super(initCapacity, loadFactor);
}
public boolean function add(Object object) {
addCount++;
return super.add(object);
}
public boolean function addAll(Collection collection) {
addCount += collection.size();
return super.addAll(collection);
}
public int function getAddCount() {
return addCount;
}
}
我只是重新实现 HashSet随着 DataHashSet类以便跟踪插入。事实上, DataHashSet继承并且是。的子类型 HashSet。我们可以,而不是HashSet,只是通过 DataHashSet(如果可能的话,用Java)。另外,我确实覆盖了基类的一些方法。Liskov Substitution原则是否合法?由于我没有对基类的行为进行任何更改,只是添加一个轨道来插入操作,这似乎是完全合法的。但是,我认为这显然是冒险的子类型和错误的代码。首先,我们应该看看添加的方法究竟会做什么。它向相关属性添加一个单元并调用父类方法。这段代码有溜溜球的问题。看着那(这 addAll方法。首先,它将收集大小添加到相关属性,然后调用addAll在父母,但父母究竟是什么 addAll做?它会多次调用add方法(循环遍历集合),但是会调用哪个add?它会在当前类或父类中添加吗?第一个是正确的。因此,计数的大小将被添加两次。当你调用addAll 时,这将发生一次;当父类调用子类中的add方法时,这将发生; 这就是我们称之为溜溜球问题的原因。这是另一个证明子类型存在风险的例子,想象一下这个例子情景:
class A {
void foo(){
...
this.bar();
...
}
void bar(){
...
}
}
class B extends A {
//override bar
void bar(){
...
}
}
class C {
void bazz(){
B b = new B();
// which bar would be called?
B.foo();
}
}
如你所见,当我们调用bazz方法时,会调用哪个栏?当然,第二个--B级中的栏将被调用。但问题是什么呢?问题是A类中的foo方法对B类中bar方法的重写一无所知。然后,你的不变量可能会被违反并且它会破坏封装,因为foo可能会期望bar方法的唯一行为自己的班级,而不是被覆盖的东西。这个问题也称为脆弱的基类问题。
实现子类型的一个更关键的问题是耦合 - 程序的一部分对另一部分(紧耦合)的不合需要的依赖。全局变量提供了强耦合导致问题的经典示例。例如,如果更改全局变量的类型,则使用该变量的所有函数(即耦合到变量)可能会受到影响,因此必须检查,修改和重新测试所有这些代码。而且,使用变量的所有函数都通过变量相互耦合。也就是说,如果变量的值在尴尬时改变,则一个函数可能会错误地影响另一个函数的行为。这个问题在多线程程序中特别棘手。
如何安全地进行子类?
子类最安全的方法是避免子类型化。如果您的类不是为子类设计的,它会通过将构造函数设为私有或向您的类添加final关键字来阻止子类化。但是如果我们想在代码中使用子类,那么我们可以创建一个包装类作为子类型的代码重用替代方法。在这种情况下,我们需要有关于代码重用的模块化推理。这涉及在不了解每个实现细节的情况下重用代码的能力。有几种方法可以处理 - 我将在这里介绍其中的一些方法。一种方法是通过将可覆盖的功能限制在少数受保护的方法中来避免可覆盖功能的自我使用。例如,使用语言机制或规范来防止覆盖其他方法。在里面DataHashSetclass,避免addAll 方法调用add。此外,我们可以通过避免在类中使用可覆盖的方法来最小化对覆盖的其他功能的直接影响。让我用前面的例子说明一下:
class A {
void foo(){
...
this.insideBar();
...
}
void insideBar(){
...
}
void bar(){
this.insideBar();
}
}
class B extends A {
//override bar
void bar(){
...
}
}
class C {
void bazz(){
B b = new B();
B.foo();
}
}
正如您在上面的代码中看到的,我只是添加了 insideBar方法,以防止由于覆盖导致的不必要的变化,并解决问题。大多数情况下,创建包装类是降低子类化风险的好方法。我的意思是,比Subtyping更喜欢组合或委托
有些地方我们必须不惜一切代价避免分类。如果我们有多种方法来进行子类型化,那么最好做委托,或者当父类中有一些无用的方法时,这意味着不需要使用扩展类(Liskov替换原则)。课程的故事是一样的; 我的意思是在重用一个类时,每当使用共享类时都不应该使用它。
超过子类型的委派
子类型模式包括一个定义实例形状的类,它充当模板。每个实例都具有类及其属性的行为,但不具有值,因为类(及其子类)的所有实例都使用存储在类中的属性的定义,并且对类中存储的属性的任何更改都将更改所有实例。
所有超类和子类都组合在一个实例中; 有线性链向上(在Java中,不是像C ++这样的语言,你可以有多个子类型)。但是,值存储在实例中,而不是存储在类中,并且不会共享它们。在子类型中,实例是独立的; 更改一个实例的状态(值)不会影响任何其他实例,并且每个实例都有自己的父对象。
委托意味着对象使用另一个对象进行方法的调用。在委托实例中,没有用于共享属性或行为的类,通常它们调用没有类的实例。对于每个类重用,您可以使用一个实例; 想象你有一个区域计算器类,它接受不同形状的区域并返回确切的区域。您只需要创建一个区域计算器对象并调用不同的区域类型类。但是在子类型中,对于每种类型的区域,您必须创建一个具有自己父级的sperate对象。
如果对象将方法或变量委托给原型,那么对这些属性或其值的任何更改都将影响对象和原型。以这种方式,委托层次结构中的对象可以彼此依赖。在委托中,您需要启动多个对象,对象可以来自不同的类型和组(与子类型相反)。此外,您需要以正确的方式组合实例以满足类需求。
此外,没有父类,因此您无法直接使用调用的对象属性。在子类型中,子类可以使用父属性或方法而无需实现,但在委派中,您必须定义一个方法来访问该属性或方法。
在委托中,重用类可以重用多个重用类。您只需要指向这些类的链接。所有这些都在同一个例子中。但是在zubtyping中,重用类是其他重用类的子类(直到下行线性链)。让我们解决问题吧DataHashSet与代表团的方法:
public class DataHashSet implements Set {
private int addCount = 0;
private Set set;
public
function DataHashSet(Set set) {
This.set = set;
}
public boolean
function add(Object object) {
addCount++;
return This.set.add(object);
}
public boolean
function addAll(Collection collection) {
addCount += collection.size();
return This.set.addAll(collection);
}
public int
function getAddCount() {
return addCount;
}
}
什么是骨骼实施?
骨架实现提供了子类型的优点而不会丧失灵活性。对于每个接口,您提供一个实现接口的抽象类,并保留未指定的基本方法。这意味着它们将方法保留为抽象并由子类实现,并且它定义了非常非原始的方法。然后,希望使用该接口的开发人员将实现该接口,然后他们通常使用骨架实现。它不如使用包装类灵活,例如组合或委托。为了使其更灵活,您可以使用一个包装类,该类将调用委托给骨架实现的匿名子类的对象。
java 子类化_如何在Java中安全地进行子类型化相关推荐
- java 正则表达式 开头_如何在Java中修复表达式的非法开头
java 正则表达式 开头 您是否遇到过这个令人难以置信的错误,想知道如何解决它? 让我们仔细阅读一下,研究如何解决表达式Java非法开头错误. 这是一个动态错误,这意味着编译器会发现某些不符合Jav ...
- java soap 头_如何在Java中添加Soap标头
我有一个来自oracle的NO.net Web服务,要访问,我需要添加soap标头.如何在Java中添加soap标头? Authenticator.setDefault(new ProxyAuthen ...
- java实现递归算法_如何在Java中实现二进制搜索算法而无需递归
java实现递归算法 by javinpaul 由javinpaul 流行的二进制搜索算法的迭代实现,用于在排序数组中查找元素. (An Iterative implementation of the ...
- java jcombobox长度_如何在JToolBar中设定JComboBox的大小?
如何在JToolBar中设定JComboBox的大小? 我设计了一个JToolBar,并且在上边添加了一个JComboBox,可是我发现这个JComboBox长度无法控制,它将JToolBar上剩余空 ...
- java 全局数组_如何在Java中声明全局数组?
我有一个程序在Java中乘以两个矩阵.我在全局错误声明中发现了一些错误. 这里是我的代码如何在Java中声明全局数组? import java.util.Scanner; /**WAP in Java ...
- java插入图片_如何在java窗体程序中添加图片
打开eclipse,创建一个java工程项目,创建完后在src下新建一个类Window,由于要插入图片,所以还在工程目录下创建一个文件夹imgs,里面放了一张60*60的图片,创建后的工程目录和图片, ...
- java安卓计时器_如何在android中设置计时器
通过java.util.Timer和java.util.TimerTask使用计时器的标准Java方法在Android中运行良好,但是你应该知道这个方法创建了一个新线程. 您可以考虑使用非常方便的Ha ...
- java cpu监控_如何在Java中监视计算机的CPU,内存和磁盘使用情况?
问题 我想用Java监视以下系统信息: 当前CPU使用率**(百分比) 可用内存*(免费/总计) 可用磁盘空间(空闲/总计)*请注意,我的意思是整个系统可用的总内存,而不仅仅是JVM. 我正在寻找一种 ...
- ubuntu java 关闭进程_如何在Ubuntu中关闭Tomcat?
问题描述 我试图关闭tomcat,如下所示,但是tomcat似乎仍在运行(http://localhost:8080 /) vandegraff@vandegraff-laptop:~$ /usr/s ...
最新文章
- 基于点云的3D障碍物检测
- WritableComparable排序案例(区内排序)
- Elastic Search 上市了,Slack上市了,我也要写个软件,走上人生巅峰
- 计算机一级汉字录入在线联系,计算机一级考试指导:汉字录入题的操作
- C#通过属性名字符串获取、设置对象属性值
- python中sqrt(4)*sqrt(9)_【单选题】Python表达式sqrt(4)*sqrt(9)的值为
- Leetcode-9-回文数(简单)
- java编程实现食堂饭卡刷卡_食堂饭卡管理系统设计方案报告.docx
- python 中无限循环_Python中如何解决无限循环的问题
- python中空字符串是什么_python为空怎么表示 python如何判断字符串为空
- 16g电脑内存有什么好处_电脑内存8G和16G有什么区别?教你区别电脑内存8G和16G...
- ​​​​​​​排列组合基本原理及公式
- 【Flutter实现表格上下左右滚动】
- seaborn画直方图、条形图、盒图、散点图等常用图形
- Android传感器常见显示程序
- C#用两种方式破解号称世界上最难的问题!爱因斯坦在20世纪初出的谜语
- Tech Talk · 云技术有话聊 | 关键基础部件如何保障高可靠?
- 看完这个就理解升压斩波(Boost)电路了
- 带语音的计算机,哪些电脑提醒软件带语音提醒?
- POJ3666(动态规划)