Java 语言的开发人员精通 C++ 和其他语言,包括多继承(multiple inheritance),使得类可以继承自任意数量的父类。多继承带来的一个问题是,不可能确定所继承的功能来自哪个父类。这个问题被称为钻石问题(请参阅 参考资料)。钻石问题和多继承中固有的其他复杂性启发了 Java 语言设计者选择 “单继承加接口” 的方法。

接口定义了语义,但没有定义行为。它们非常适合用来定义方法签名和数据抽象,所有 Java 下一代语言都支持 Java 接口,并且无需进行重大的修改。不过,有些交叉问题不适合使用 “单继承加接口” 模型。这种错位导致必须提供适合 Java 语言的外部机制,比如面向方面的编程。两种 Java 下一代语言(Groovy 和 Scala)通过使用一种被称为混入 或特征 的语言结构在另一个层次的扩展上处理这类问题。本文介绍了 Groovy 中的混入和 Scala 中的特征,并演示了如何使用它们。(Clojure 通过协议处理大致相同的功能,我在 Java 下一代:没有继承性的扩展,第 2 部分 中已经介绍过这一点。)

混入

冰淇淋的灵感

混入的概念起源于 Flavors 语言(请参阅 参考资料)。这个概念的灵感来自于开发该语言的办公室附近的一家冰​​淇淋店。这家冰淇淋店提供了纯口味的冰淇淋,以及客户想要的其他任何的 “混合物”(糖果碎、糖屑、坚果,等等)。

早期的一些面向对象语言在单个代码块中共同定义某个类的属性和方法,所有类定义是完整的。在其他语言中,开发人员可以在一个地方定义属性,但推迟方法的定义,并在适当的时候将它们 “混合” 到类中。随着面向对象语言的演变,混入与现代语言的配合方式的细节也在演变。

在 Ruby、Groovy 和类似的语言中,作为一个接口和父类之间的交叉,混入可以扩充现有的类层次结构。像接口一样,混入可以充当 instanceof 检查的类型,同时也要遵循相同的扩展规则。您可以将无限数量的混入应用于某一个类。与接口不同的是,混入不仅指定了方法签名,也可以实现签名的行为。

在包括混入的第一种语言中,混入只包含方法,不包含状态,例如,成员变量。现在很多语言(Groovy 也在其中)都包括有状态的混入。Scala 的特征也以有状态的方式进行操作。

回页首

Groovy 的混入

Groovy 通过 metaClass.mixin() 方法或 @Mixin 注解来实现混入。(@Mixin 注解依次使用 Groovy Abstract Syntax Tree (AST) 转换,以支持所需的元编程管道。)清单 1 中的示例使用 metaClass.mixin() 让 File 类能够创建 ZIP 压缩文件:

清单 1. 将 zip() 方法混合到 File 类中

class Zipper {

def zip(dest) {

new ZipOutputStream(new FileOutputStream(dest))

.withStream { ZipOutputStream zos ->

eachFileRecurse { f ->

if (!f.isDirectory()) {

zos.putNextEntry(new ZipEntry(f.getPath()))

new FileInputStream(f).withStream { s ->

zos << s

zos.closeEntry()

}

}

}

}

}

static {

File.metaClass.mixin(Zipper)

}

}

hxbylc89.com

slylc843.com

sleylc63.com

jbpylc56.com

mpylc86.com

在清单 1 中,我创建了一个 Zipper 类,它包含新的 zip() 方法,以及将该方法添加到现有 File 类的连接。zip() 方法的(不起眼的)Groovy 代码以递归方式创建了一个 ZIP 文件。清单的最后一部分通过使用静态的初始化程序,将新方法添加到现有的 File 类。在 Java 语言中,类的静态初始化程序在加载类的时候运行。静态初始化程序是扩充代码的理想位置,因为在运行依赖于增强的任何代码之前,应确保先运行初始化程序。在 清单 1 中,mixin() 方法将 zip() 方法添加到 File。

在 "没有继承性的扩展,第 1 部分" 中,我介绍了两种 Groovy 机制: ExpandoMetaClass 和类别类,您可以使用它们在现有类上添加、更改或删除方法。使用 mixin() 添加方法的最终结果与使用 ExpandoMetaClass 或类别类添加方法的最终结果相同,但它们的实现是不一样的。请考虑清单 2 中混入示例:

清单 2. 混入操纵继承层次结构

import groovy.transform.ToString

class DebugInfo {

def getWhoAmI() {

println "${this.class}

<

}

}

@ToString class Person {

def name, age

}

@ToString class Employee extends Person {

def id, role

}

@ToString class Manager extends Employee {

def suiteNo

}

Person.mixin(DebugInfo)

def p = new Person(name:"Pete", age:33)

def e = new Employee(name:"Fred", age:25, id:"FRE", role:"Manager")

def m = new Manager(name:"Burns", id:"001", suiteNo:"1A")

p.whoAmI

e.whoAmI

m.whoAmI

在清单 2 中,我创建了一个名为 DebugInfo 的类,其中包含一个 getWhoAmI 属性定义。在该属性内,我打印出类的一些详细信息,比如当前类以及 super 和 getClass().getSuperClass() 属性的父子关系说明。接下来,我创建一个简单的类层次结构,包括 Person、Employee 和 Manager。

然后我将 DebugInfo 类混合到驻留在层次结构顶部的 Person 类。由于 Person 具有 whoAmI 属性,所以其子类也具有该属性。

在输出中,可以看到(并且可能会感到惊讶),DebugInfo 类将自己插入到继承层次结构中:

class Person

class Employee

class Manager

混入方法必须适应 Groovy 中现有的复杂关系,以便进行方法解析。清单 2 中的父类的不同返回值反映了这些关系。方法解析的细节不属于本文的讨论范围。但请小心处理对混入方法中的 this 和 super 值(及其各种形式)的依赖。

使用类别类或 ExpandoMetaClass 不影响继承,因为您只是对类进行修改,而不是混入不同的新行为中。这样做的一个缺点是,无法将这些更改识别为一个不同类别的构件。如果我使用类别类或 ExpandoMetaClass 将相同的三个方法添加到多个类中,那么没有特定的代码构件(比如接口或类签名)可以识别目前存在的共性。混入的优点是,Groovy 将使用混入的一切都视为一个类别。

类别类实现的一个麻烦之处在于严格的类结构。您必须完全使用静态方法,每个方法至少需要接受一个参数,以代表正在进行扩充的类型。元编程是最有用的,它可以消除这样的样板代码。@Mixin 注释的出现使得创建类别并将它们混合到类中变得更容易。清单 3(摘自 Groovy 文档)说明了类别和混入之间的协同效应:

清单 3. 结合类别和混入

interface Vehicle {

String getName()

}

@Category(Vehicle) class Flying {

def fly() { "I'm the ${name} and I fly!"}

}

@Category(Vehicle) class Diving {

def dive() { "I'm the ${name} and I dive!"}

}

@Mixin([Diving, Flying])

class JamesBondVehicle implements Vehicle {

String getName() { "James Bond's vehicle" }

}

assert new JamesBondVehicle().fly() ==

"I'm the James Bond's vehicle and I fly!"

assert new JamesBondVehicle().dive() ==

"I'm the James Bond's vehicle and I dive!"

在清单 3 中,我创建了一个简单的 Vehicle 接口和两个类别类(Flying 和 Diving)。@Category 注释关注样板代码的要求。在定义了类别之后,我将它们混合成一个 JamesBondVehicle,以便连接两个行为。

类别、ExpandoMetaClass 和混入在 Groovy 中的交集是积极的语言进化的必然结果。三种技术明显有重叠之处,但每种技术都有它们自身才能处理得最好的强项。如果从头重新设计 Groovy,那么作者可能会将三种技术的多个特性整合在一个机制中。

java 下一代_Java 下一代: 混入和特征相关推荐

  1. 【源码+教程】Java课设项目_12款最热最新Java游戏项目_Java游戏开发_Java小游戏_飞翔的小鸟_王者荣耀_超级玛丽_推箱子_黄金矿工_贪吃蛇

    马上就要期末了,同学们课设做的如何了呢?本篇为大家带来了12款热门Java小游戏项目的源码和教程,助力大家顺利迎接暑假![源码+教程]Java课设项目_12款最热最新Java游戏项目_Java游戏开发 ...

  2. 黑马程序员全套Java教程_Java基础教程_异常(含扩展)(二十三)

    黑马程序员全套Java教程_Java基础教程_异常(含扩展)(二十三) 1.1 异常概述与异常体系结构 1.2 JVM遇到异常时的默认处理方案 1.3 异常处理 1.4 异常处理之try--catch ...

  3. java 原子量_Java線程:新特征-原子量

    所謂的原子量即操作變量的操作是"原子的",該操作不可再分,因此是線程安全的. 為何要使用原子變量呢,原因是多個線程對單個變量操作也會引起一些問題.在Java5之前,可以通過vola ...

  4. java五大原则_Java面向对象的三大特征和五大原则

    Java面向对象的三大特征 封装 封装(Encapsulation)是指属性私有化,根据需要提供setter和getter方法来访问属性.即隐藏具体属性和实现细节,仅对外开放接口,控制程序中属性的访问 ...

  5. 对java面向对象的三大特征的理解_Java面向对象的三大特征是什么?

    面向对象的三大核心特性简介 面向对象开发模式更有利于人们开拓思维,在具体的开发过程中便于程序的划分,方便程序员分工合作,提高开发效率. 该开发模式之所以使程序设计更加完善和强大,主要是因为面向对象具有 ...

  6. java面向对象基本特征_Java 面向对象的基本特征

    前言: 在刚开始接触Java的时候,那时候面对Java面向对象的几大特征一直理解的不是很理解,借着空闲时间在这里整理一下,同时在加深一下印象. 一.封装: Java面向对象的特征之封装,所谓的封装就是 ...

  7. java面向对象认定的特征_Java面向对象的三大特征(一)

    在Java的学习和使用过程中都要面对一个问题--->对象.我们程序员是最不缺对象的,需要时就new一个,简单.方便.快捷.在生活中任何人都不能说Java程序员没对象,因为我们可以随时new出一个 ...

  8. java制表符_Java地位无可动摇的12个原因

    如今,面对曾经在程序员中被各种新技术掩盖直至堙灭的技术值得怀念.犹如COBOL这当年被老程序员们尊为神器的语言如今也基本没有价值.而Java作为现代程序员的中坚力量在这点上会不会成为下一个COBOL? ...

  9. java生气_Java来抢饭碗,C++可别生气

    Java来抢饭碗,C++可别生气 Java之好,因为它描绘了Internet的未来:Java之坏,因为它是Sun的私有财产,是一家之言.看过<Thinking in Java>这本权威教学 ...

最新文章

  1. Codeforces Round #554 (Div. 2) C. Neko does Maths (简单推导)
  2. 嵌入式linux系统架构
  3. kaldi在java中运行_ubuntu下kaldi的安装以及实例的运行
  4. Java实现ftp的上传、下载和删除
  5. vue如何使用原生js写动画效果_深入理解 Vuejs 动画效果
  6. sqoop 导入到hive字段全是null_Sqoop 一点通
  7. 2020-01-14 转载【dpdk】使用libpcap-PMD驱动收发包
  8. 【算法学习】Fast burst images denoising
  9. 2017年2月14日
  10. 亲,愚人节要来了!记得带着智商出门哦
  11. Java后端面试题总结一
  12. 神通广大、卓有奇效的“mini-KMS_Activator_v1.051”(迷你KMS)初现江湖
  13. Android基于Ymodem协议升级嵌入式MCU主控
  14. 老徐和阿珍的故事:缓存穿透、缓存击穿、缓存雪崩、缓存热点,傻傻分不清楚
  15. 英文原始文本的读取与处理
  16. 计算机excel中钱的符号,在excel中输入钱的符号
  17. 矩阵求和 c语言 简单易懂
  18. 【随笔记】Deepin20 安装docker
  19. Android10.0修改默认usb为MTP模式
  20. 玫瑰李文案:玫瑰李水果店的文案怎么写

热门文章

  1. charles抓包显示乱码解决方法
  2. 如何知道对象在Python中是否具有属性
  3. Win10重装系统后如何合并分区?
  4. win11如何快速加密硬盘 Windows11快速加密硬盘的设置方法
  5. 终结者2显示天网服务器,《终结者2:审判日》天网觉醒秘测开服公告
  6. 端如何访问rc_如何进行 Linux 启动时间优化
  7. 洛谷——P1000 超级玛丽游戏
  8. 实验4-1-9 猜数字游戏 (15 分)
  9. 海康相机回调方式理一下
  10. Uncaught (in promise) Error: Avoided redundant navigation to current location: “/index“. 解决方法