深切了解abstract class和interface


立马首文章看罢只是针对Groovy有所了解,只是当作学Gradle的放到知识。
安问题要圈布局Groovy开发条件(Windows)。

abstract
class和interface是Java语言中对此肤浅类定义进行支撑的个别栽体制,正是出于当下片种植机制的是,才给了Java强大的面向对象能力。abstract
class和interface之间以对肤浅类定义的支持方面负有非常非常之相似性,甚至足以彼此替换,因此多开发者在开展抽象类定义时对于abstract
class和interface的抉择显得比较随意。其实,两者之间还是出充分要命的区分的,对于它的挑还是反映出对问题领域本质的晓、对于规划意图的知是否对、合理。本文将对其中间的界别展开一番解析,试图给开发者提供一个于二者之间进行选择的基于。

简史

Groovy的1.0本发布于2007年1月2日。
2012年之年中,Groovy的2.0版本发布了。
脚下最新版本是2.4.4。


Java的物Groovy都能就此,包括语法和类库

例如,新建一个SuperTest.groovy
输入一下Java代码:

public class SuperTest {

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            System.out.println("Java的东西Groovy都能用");
        }
        System.out.println("随便输入点啥试试");
        Scanner sc = new Scanner(System.in); // 不需要导入java.util包
        String str = sc.next();
        System.out.println("你刚才输入了:" + str);
    }

}

运行结果:

Java的东西Groovy都能用
Java的东西Groovy都能用
Java的东西Groovy都能用
随便输入点啥试试
放开那条PM,让如花来
你刚才输入了:放开那条PM,让如花来

了解抽象类

abstract
class和interface在Java语言中还是为此来进展抽象类(本文中的抽象类并非由abstract
class翻译而来,它象征的凡一个抽象体,而abstract
class为Java语言中用来定义抽象类的同一种方式,请读者注意区分)定义的,那么什么是抽象类,使用抽象类能为我们带什么补也?

当面向对象的概念受到,我们掌握有的靶子都是由此类似来描写的,但是反过来也不是这般。并无是怀有的切近都是因此来描写对象的,如果一个好像中从不包含足够的消息来描写一个具体的对象,这样的类即是抽象类。抽象类往往用来表征我们当针对题目领域展开剖析、设计着得出的抽象概念,是对准相同密密麻麻看上去不同,但是精神上等同之求实概念的抽象。比如:如果我们进行一个图纸编辑软件之开,就见面发觉题目领域存在正在全面、三角形这样有现实概念,它们是殊的,但是其而都属形状这样一个定义,形状是定义在题目领域是无设有的,它便是一个抽象概念。正是为虚无的概念在题目领域尚未对应的有血有肉概念,所以用以表征抽象概念的抽象类是免能够实例化的。

在面向对象领域,抽象类主要用以进行路隐藏。我们得以组织出一个永恒的同等组行为之泛描述,但是这组行为也会起自由只可能的实际实现方式。这个抽象描述就是是抽象类,而这无异于组随机个或的求实实现则呈现也有着可能的派生类。模块可操作一个抽象体。由于模块依赖让一个恒定的抽象体,因此它们好是勿允许修改的;同时,通过自者抽象体派生,也不过扩大此模块的行为功能。熟悉OCP的读者必定懂,为了能够实现面向对象设计之一个最好中心之规范OCP(
Open-Closed Principle),抽象类是内部的关键所在。


自行导入常用包

在上面的例子里应用了Scanner却无欲导入java.util包,是以Groovy自动导入下列包:
java.lang
java.util
java.io
java.net
java.math.BigDecimal
java.math.BigInteger
groovy.lang
groovy.util

自语法定义层面看abstract class和interface

于语法层面,Java语言对于abstract
class和interface给有了不同的定义方式,下面坐定义一个称作也Demo的抽象类为例来说明这种不同。

以abstract class的措施定义Demo抽象类的艺术如下:

abstract class Demo {
    abstract void method1();
    abstract void method2();
    …
}

动interface的不二法门定义Demo抽象类的方式如下:

interface Demo {
    void method1();
    void method2();
    …
}

以abstract
class方式中,Demo可以出谈得来之数据成员,也得以产生非abstarct的积极分子方法,而于interface方式的兑现着,Demo只能够产生静态的免克给改的数码成员(也就算是必是static
final的,不过在interface中一般不定义数据成员),所有的积极分子方法还是abstract的。从某种意义上说,interface是千篇一律种异常形式的abstract
class。

于abstract
class和interface在语法定义层面又多之细节问题,不是本文的要,不再赘述,读者可参照参考文献〔1〕获得更多的系内容。


Groovy特点

自从以上Groovy可以实行Java的例子可以感受及到以下简单独特色:

  • Groovy继承了Java的有所东西,就是您突然忘了Groovy的语法可以描绘成Java代码,也便是Groovy和Java混在联名吧能够执行。
  • Groovy和Java一样运行在JVM,源码都是事先编译为class字节码。

而外,Groovy又针对Java的语法进行了简化,功能拓展了扩大。这个得学了切实语法才会感受及,不过好优先来感触下和Groovy相比Java有差不多啰嗦,就将点的循环三全的例证来说,以下的代码实现了千篇一律的效用:

for (i in 0..2) {
    println 'Java的东西Groovy都能用'
}

或者

3.times {
    println 'Java的东西Groovy都能用'
}

———————–开始语法内容———————–

由编程层面看abstract class和interface

于编程的角度来拘禁,abstract class和interface都得就此来促成”design by
contract”的思想。但是于切实可行的运方面还是产生部分界别的。

率先,abstract
class在Java语言中意味的凡如出一辙种持续关系,一个类似只能动用相同不善连续关系。但是,一个好像可得以兑现多只interface。也许,这是Java语言的设计者在考虑Java对于多复继承的支持地方的一样种折中考虑吧。

从,在abstract
class的概念着,我们得以授予方法的默认行为。但是当interface的概念着,方法也未可知具备默认行为,为了绕了之范围,必须以委托,但是就会
增加一些错综复杂,有时见面招大挺之辛苦。

以空洞类中无克定义默认行为还设有任何一个比较严重的问题,那即便是可能会见招致维护上之麻烦。因为要是后来想修改类的界面(一般通过abstract
class或者interface来代表)以适应新的状况(比如,添加新的计要叫曾经用底措施吃补充加新的参数)时,就会见生之麻烦,可能要花费很多底时空(对于派生类很多的动静,尤为如此)。但是只要界面是经过abstract
class来贯彻之,那么可能就独自需要修改定义在abstract
class中之默认行为就是可了。
同等,如果非可知于空洞类吃定义默认行为,就会见导致同的计实现有现在欠抽象类的诸一个派生类中,违反了”one
rule,one
place”原则,造成代码重复,同样未便于以后的保障。因此,在abstract
class和interface间进行精选时只要特别之小心。


关键字

<code>
as、assert
break
case、catch、class、const、continue
def、default、do
else、enum、extends
false、finally、for
goto
if、implements、import、in、instanceof、interface
new、null
package
return
super、switch
this、throw、throws、trait、true、try
while
</code>

由统筹意见层面看abstract class和interface

地方根本从语法定义跟编程的角度阐述了abstract
class和interface的分别,这些层面的分是比较没有层次的、非本质的。本小节拿于另外一个规模:abstract
class和interface所反映出底设计意见,来分析一下两岸的区别。作者认为,从夫层面进行剖析才能够清楚两者概念的原形所在。

前面已经关系过,abstarct
class在Java语言中体现了扳平栽持续关系,要惦记使后续关系成立,父类和派生类之间要在”is
a”关系,即父类和派生类在概念本质上相应是一律之(参考文献〔3〕中产生有关”is
a”关系的不行篇幅深入之阐述,有趣味的读者可以参见)。对于interface
来说则不然,并无求interface的实现者和interface定义在概念本质上是千篇一律的,仅仅是促成了interface定义的契约而已。为了使论述便于理解,下面用经过一个简单的实例进行说明。

设想这样一个事例,假要于咱们的问题领域被有一个关于Door的抽象概念,该Door具有行两个动作open和close,此时我们可由此abstract
class或者interface来定义一个意味该抽象概念的类,定义方式分别如下所示:

动abstract class方式定义Door:

abstract class Door {
    abstract void open();
    abstract void close();
}

下interface方式定义Door:

interface Door {
    void open();
    void close();
}

另外实际的Door类型可以extends使用abstract
class方式定义之Door或者implements使用interface方式定义之Door。看起好像使abstract
class和interface没有那个之界别。

苟今天求Door还要具有报警的功用。我们欠怎么统筹针对性该例子的好像组织吧(在本例中,主要是为显得abstract
class和interface反映在筹划理念上的分别,其他方无关之题材还做了简化或忽视)?下面用陈有或的缓解方案,并于筹划意见层面对这些不同的方案展开解析。

缓解方案一:

简而言之的于Door的概念着加进一个alarm方法,如下:

abstract class Door {
    abstract void open();
    abstract void close();
    abstract void alarm();
}

或者

interface Door {
    void open();
    void close();
    void alarm();
}

这就是说富有报警功能的AlarmDoor的定义方式如下:

class AlarmDoor extends Door {
    void open() { … }
    void close() { … }
    void alarm() { … }
}

或者

class AlarmDoor implements Door {
    void open() { … }
    void close() { … }
    void alarm() { … }
}

这种办法违反了面向对象设计被之一个骨干标准ISP(Interface Segregation
Priciple),在Door的定义着管Door概念本身固有之行事艺术和另外一个定义”报警器”的表现方式混在了并。这样引起的一个问题是那些单纯凭借让Door这个定义的模块会因为”报警器”这个概念的变动(比如:修改alarm方法的参数)而改,反的还是。

解决方案二:

既然open、close和alarm属于有限单例外的定义,根据ISP原则应该把她各自定义在象征立即点儿独概念的泛类中。定义方式产生:这片单概念都应用abstract
class方式定义;两只概念都利用interface方式定义;一个定义使用abstract
class方式定义,另一个概念使用interface方式定义。

妇孺皆知,由于Java语言不支持多再继承,所以个别只概念都动abstract
class方式定义是不可行的。后面两种艺术还是可行之,但是于其的选取也体现来对于问题领域被之概念本质的懂得、对于规划意图的反映是否科学、合理。我们挨个来分析、说明。

一经简单只概念都用interface方式来定义,那么即便反映出点儿个问题:
1、我们可能没了解掌握问题领域,AlarmDoor于概念本质上到底是Door还是报警器?
2、如果我们对此问题领域的掌握没有问题,比如:我们经过对问题领域的辨析发现AlarmDoor在概念本质上同Door是同等的,那么我们在实现时即从不能够正确的颁布我们的规划意图,因为当及时半独概念的概念及(均采取interface方式定义)反映无发出上述意义。

一旦我们于问题领域的知情是:AlarmDoor在概念本质上是Door,同时其发有报警的机能。我们欠怎么来统筹、实现来家喻户晓的反映出我们的意思呢?前面已经说了,abstract
class在Java语言中意味无异栽持续关系,而连续关系在精神上是”is
a”关系。所以对Door这个定义,我们应当利用abstarct
class方式来定义。另外,AlarmDoor又兼备报警功能,说明其又能好报警概念遭到定义之行,所以报警概念可以经interface方式定义。如下所示:

abstract class Door {
    abstract void open();
    abstract void close();
}
interface Alarm {
    void alarm();
}
class AlarmDoor extends Door implements Alarm {
    void open() { … }
    void close() { … }
    void alarm() { … }
}

这种实现方式大多能明确的反映出我们对此问题领域的掌握,正确的发布我们的计划性意图。其实abstract
class表示的凡”is a”关系,interface表示的是”like
a”关系,大家以甄选时得作为一个根据,当然这是确立在对题目领域的明白上的,比如:如果我们觉得AlarmDoor在概念本质上是报警器,同时以拥有Door的效果,那么上述的定义方式就设掉了。


报句不需要分号结尾

形容了邪没事

结论

abstract
class和interface是Java语言中的简单栽概念抽象类的方式,它们之间发生非常死的相似性。但是对她的挑选也又屡次体现来对于问题领域面临之概念本质之知道、对于规划意图的体现是否正确、合理,因为她表现了定义里的不等的涉及(虽然都能实现需求的成效)。这实质上也是语言的均等种植之惯用法,希望读者对象能细细体会。


注释

// 单行注释
println "hello groovy," /* 我是块注释 */ + "my name is xuhongchuan."

hello groovy,my name is xuhongchuan.

参考资料

[1] Thinking in Java, Bruce Eckel
[2] Design Patterns Explained: A New Perspective on Object-Oriented
Design, Alan Shalloway and James R. Trott
[3] Effective C++: 50 Specific Ways to Improve Your Programs and
Design, Scott Meyers


标示符

和Java一样:
提议只有所以假名、数字、美元$和下划线组成。
因字母,美元符号$或者下划线
开始,不可知为数字开。

概念变量

因此def定义变量,不写def也行。
概念变量不用指定数据类型且可肆意变换。

def var1 = 1024
var2 = "def不写也行"

var1 = "Integer 改 String"

定义方法

定义方法也是因此def当然也得不写。

// 使用def
def String getName() {
    return "许宏川"
}

// 不写def
String getName() {
    return "许宏川"
}

Groovy所有的道还来返回路,如果非写虽然回null,没有void。返回路可以概括不写,不写的说话自动取于最后一行代码的门类。
匪写返回路的方法就是亟须长def。
另外return也足以概括不写,都是赢得最后一行。

def getName() {
    "许宏川"
    1234 // 这行是最后一行,返回Integer
}

方法参数类型可写不过免写

// param1写了数据类型, 调用时必须传进来String参数
// param2没写数据类型,调用时可以传进来任意类型的参数
def getString(String param1, param2) {

}

public是默认的

Groovy的接近及章程的默认修饰符都是public,且可略不写。由于修饰符可以略、方法返回路可以简简单单、方法参数类型可以简单。所以Java的近乎以及main方法的布局得以简化为:

class SuperTest {

    static main(args) {

    }

}

竟也堪无写类及main结构,直接写main方法里之代码。编译成class文件时见面自动为上加上。

字符串

分开三栽,单引号,双引号和三引号三种植。

  • 单引号是输入什么就是啊。
    例如:

println('my name is $ xuhongchuan')

打印结果吧:

my name is $ xuhongchuan

$也正常打印出了。

  • 如复引号可以用$引用变量的值。
    例如

name = "xuhongchuan" 
println("my name is $name")

打印结果吧:

my name is xuhongchuan
  • 老三逗号是出口一段子文本,可以一直的加空格和换行。
    例如:

println('''这是一段文本。
换行啦!!!
    前面有四个空。。。
有换行啦!!!!''')

打印结果吧:

这是一段文本。
换行啦!!!
    前面有四个空。。。
有换行啦!!!!

本条好哎,想想写sql语句时只是轻易换行有差不多舒畅。

数据类型

细分基本数据列、容器与闭包三种植。

中心数据类

Groovy是纯粹面向对象的语言,没有Java里的byte、int、double、boolean等八个值类型。但是有相应的包类设Integer、Double和Boolean。其中整数默认是Integer,浮点数默认是。。。你想就是Double?不,是BigDecimal。
若是想显式指定Long类型,在末端加L,Double则加D。

var = 5
println var.class

var = 5.5
println var.class

var = 5L
println var.class

var = 5D
println var.class

var = 'hehe'
println var.class

var = false
println var.class

输出结果为:

class java.lang.Integer
class java.math.BigDecimal
class java.lang.Long
class java.lang.Double
class java.lang.String
class java.lang.Boolean

容器类

分List、Map和Range。

  • List

def demoList = [121, 3.14, 'hello', false, null] // 使用[]定义,元素之间用,隔开
println demoList.size // 获取集合大小
prinltn demoList[2] // 获取index为2的元素
// 在结尾添加元素的两种写法
demoList.add(100) 
demoList << 100
//在指定位置添加元素,原本index大于等于3的元素往后退一位
demoList.add(3, 100)
demoList.remove(0) // 删除指定index的元素
demoList -= [3.14, false] // 删除某集合的元素
demoList.clear() // 清空集合
// 使用集合直接调用.each可以对集合进行遍历
demoList.each {
    println it // it是迭代过程中的每一个元素
}
  • Map
    使用[key : value]概念,元素中为此,隔开。
    key必须是String,也得无加以引号自动转为String。

def demoMap = ['name' : '许宏川', 'age' : 18, 'isGay' : false]
println demoMap.size() // 获取map大小
println demoMap.name // 通过key获取值
demoMap << ['hehe' : '777'] // 添加元素
// 遍历map
demoMap.each {
    println it.key
    println it.value
}
  • Range

// 范围从1到10
def demoRange = 1..10
// 范围从1到9
def demoRange2 = 1..<10
println(demoRange2.from) // 获取起始值
println(demoRange2.to) // 获取最大值

闭包

闭包是一样截取代码块,注意闭包也是数据类型,所以可以将闭包作为艺术的参数或者返回路。
一旦我们而筛选指定数n范围外之奇数,普通写法如下:

def getOdd(n) {
    for (i in 1..n) {
        if (i % 2 != 0)
            println i
    }
}

getOdd(10)

若果假定落偶数,又比方又写一个计:

def getEven(n) {
    for (i in 1..n) {
        if (i % 2 == 0)
            println i
    }
}

getEven(10)

旋即简单只点子其实for循环部分的情是重合的。
假设只要就此闭包就无会见这样了,例如下面的pick接受两单参数,一个参数n,另外一个是闭包(closure是变量名随便取)。再又相同整个闭包是一个代码块,这里传上你想以遍历过程做呀。至于怎么把惠及过程的i传递给闭包,闭包有一个隐式变量叫it,可以收到一个参数。
看代码:

def pick(n, closure) {
    for (i in 1..n) {
        closure(i)
    }
}

// 打印奇数
pick(10, {
    if (it % 2 != 0) // it代表传进来的参数,也就是上面closure(i)的i
            println it
})

// 打印偶数
pick(10, {
    if (it % 2 == 0)
        println it
})

总之循环结构不需团结写了,你一味待写你想在遍历过程中举行啊,例如如果一旦打印全部屡屡底平方可以如此:

// 平方
pick(10, {
    println it **= 2
})

以此时节善于思考的同学便假设问了,我还要协调写这些行为?
亲自,这不就是是动态的魅力么,谁知道您要是当遍历过程做呀吧?但是倘若起一部分表现是不时用之,你为受闭包取个名固定下来啊就像定义变量一样。
譬如说如果拿刚刚底底打印奇数、打印偶数和打印平方定义成变量可以变更成为这样:

def pick(n, closure) {
    for (i in 1..n) {
        closure(i)
    }
}

// 打印奇数
def getOdd = {
    if (it % 2 != 0)
        println it
}

// 打印偶数
def getEven = {
    if (it % 2 == 0)
        println it
}

// 打印平方
def getSquare = {
    println it **= 2
}

pick(10, getOdd)
pick(10, getEven)
pick(10, getSquare)

夫时段,善于思考的校友又要咨询了,隐式变量it只能表示一个参数吧?闭包怎么收多个参数?
凡这样的,用 ->
把参数列表和表现隔开即可。假设我们定义一个闭包接受两个参数求他们的与:

def getSum = {
    x, y -> println x + y
}

getSum(3, 4) // 闭包可以直接调用

有关闭包还发出只说之,就是只要你的闭包不需要收取参数,但是还是会自动生成隐式it,只不过它的值为null。也就是说,闭包至少含有一个参数。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图