继承固然非常强大,但我们不能滥用继承,如使用不当,则会引起诸多问题.只有当子类和超类之间确实存在"is-a"关系的时候,使用继承才是恰当的.即便如此,如果子类和超类在不同的包中,并且超类不是为了继承而设计的,那么继承将会是脆弱的.此时,我们应该考虑用包装类,一复合和转发机制来代替继承.
让我们来看个具体的例子: 我们假设程序用了个HashSet, 我们想统计一个HashSet对象创建以来总共加了多少个元素,为了实现这个功能,我们首先考虑继承,然后覆盖原有的实现加个计数:
package com.effective.item16;
import java.util.Collection;
import java.util.HashSet;
public class InstrumentedHashSet<E> extends HashSet<E> {
private static final long serialVersionUID = 1L;
private int count = 0;
InstrumentedHashSet() {
super();
}
@Override
public boolean add(E e){
count++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c){
count += c.size();
return super.addAll(c);
}
public int getCount(){
return count;
}
}
结果发现但我们调用如下代码时,计数本应为3,真实结果为6. 什么原因呢? 原来父类HashSet里面addAll方法调用了add方法.这种内部细节,我们作为api的使用者很难知道,除非查看源码. 继而又有个严重的问题,如果HashSet的具体实现改变了,那么又有可能引起其他的问题,很难察觉.
让我们换种思路,采用复合代替继承.
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
public class WrapperSet<E> implements Set<E> {
private int count = 0;
private Set<E> s;
private WrapperSet(Set<E> s) {
super();
this.s = s;
}
...
@Override
public boolean add(E e) {
count++;
return s.add(e);
}
@Override
public boolean remove(Object o) {
return s.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return c.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
if(c != null)
count += c.size();
return s.addAll(c);
}
...
}
Set接口保存了HashSet的功能特性,看似麻烦了些,但程序变得健壮了,这种Wrapper设计也带来了灵活性,他可以包装任何的Set实现, 同时又很好的扩展了计数器.
关于包装类,大家可以结合Decorator模式和Proxy模式做更深入的研究.
分享到:
相关推荐
- **比较**:包装类实现了Comparable接口,可以使用equals()方法进行对象间的等价比较,以及compareTo()方法进行数值大小的比较。 - **缓存机制**:对于Integer、Character、Byte、Short、Boolean,当数值在-128到...
在Java编程语言中,接口(Interface)和包装类(Wrapper Class)是两个核心概念,它们在实际开发中扮演着至关重要的角色。接口提供了一种定义规范的方式,而包装类则是基本数据类型与对象世界之间的桥梁。现在让我们...
包装类的主要作用是将基本数据类型转换为对象,以满足Java中只针对对象设计的API和新特性的需求,如泛型、接口等。在面向对象编程中,包装类提供了更多的功能和便利。 二、包装类的使用 1. 自定义包装类:虽然Java...
- `compareTo()`:实现Comparable接口,用于比较两个包装类对象的大小。 - `hashCode()`和`equals()`:定义了对象的哈希码和相等性检查,符合Java对象的一般约定。 - `parseInt(String s)`:解析字符串为指定类型...
例如Byte、Short、Integer、Long、Float、Double、Character、Boolean 等包装类。这些类可以将基本数据类型转换为对象。 七、应用场景 抽象类和接口可以应用于多种场景,例如图形界面程序编程、事件驱动程序、异常...
这些包装类不仅提供了丰富的操作接口,还能够帮助开发者更加方便地处理基本数据类型与对象之间的转换。 #### 包装类列表 Java为以下基本数据类型提供了对应的包装类: - `byte` - `Byte` - `short` - `Short` - `...
行业分类-外包设计-用于包装微流体装置的接口件的介绍分析.rar
- 封装是面向对象的三大特性之一,包装类实现了对基本数据类型的封装,隐藏了内部细节,提供了对外接口进行操作。 - 通过封装,可以确保数据的完整性和安全性,防止直接对基本类型进行错误的修改。 6. **包装类的...
SceneLib将复杂的OpenGL调用包装成更易于理解和使用的类和函数,从而提高开发效率。 1. **SceneLib的结构** SceneLib通常会包含多个核心组件,如渲染器、场景图、相机、光源、材质、纹理等。这些组件以面向对象的...
面向对象编程(Object-Oriented Programming, OOP)是基于对象和类的编程范式,它强调将数据结构和算法结合为一个整体,以更自然地反映现实世界的问题。早期的程序设计方法,如结构化编程,将算法和数据结构视为独立...
面向对象程序设计中,可以将一个类的定义 放在另一个类的内部,这就是内部类(有的地方叫嵌套类), 包含内部类的类也被称为外部类(有的地方也叫宿主类...但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
- Java提供了对应的包装类(如`Integer`、`Double`等)用于封装基本类型,并且在某些情况下,系统会自动进行基本类型与包装类之间的转换。 8. **BigInteger和BigDecimal类**: - 这两个类提供了任意精度的整数和...
包装类为Java的基本数据类型提供了面向对象的接口,使得我们可以将基本类型的值封装到对象中,进而可以在需要对象的地方使用。 一、包装类概述 包装类的存在主要解决了以下两个问题: 1. **参数传递**:由于Java是...
Java API基础包括java.lang包中的基础类库,例如Object类、Math数学类、Comparable可比较接口、基本数据类型的包装类、String字符串类、Class类操作类和System系统类等。这些类库提供了Java语言的基础功能和工具。 ...
8个包装类实现的接口:Serializable ,Comparable 3、构造方法 前面6个包装类、Boolean 1、本身常量值的一个构造方法 (value) 2、带String 参数一个构造方法(String s) Character Character(char ...
抽象类和接口是Java编程语言中的两种重要概念...`Calendar`和`GregorianCalendar`处理日期和时间,包装类和`BigInteger`、`BigDecimal`处理数值类型,这些类和接口共同构成了Java丰富的标准库,为日常开发提供了便利。
例如,许多内置的包装类(如 `Integer` 和 `String`)都实现了 `Comparable` 接口。 **自定义接口** 是程序员根据需求创建的接口,可以定义特定的行为或者约定。自定义接口有助于保持代码的模块化和可扩展性。 **...
在 Java 语言中,基本类型和包装类类型之间可以自动转化。例如,int 类型可以自动转化为 Integer 类型,double 类型可以自动转化为 Double 类型。这种自动转化可以简化程序设计,提高编程效率。 使用 BigInteger 和...
5. API(Application Programming Interface)设计:创建对外接口,使其他系统和服务能够方便地获取和使用包装好的数据。 在实际操作中,外包设计团队需要与客户紧密合作,了解业务需求,定义数据模型,选择合适的...