前言
这一篇我们主要讲解Groovy
基础语法的最后一部分面向对象
,我们将从以下几个部分来讲解
Groovy
类、接口等的定义和使用
这一部分和我们的Java
基本没有什么变化,我们首先创建一个Person
类,给它指定两个属性name
和age
,同时在Person
类里面定义一个方法increaseAge
/**
* 1.Groovy中默认都是public
*/
class Person {
String name
Integer age
def increaseAge(Integer years) {
this.age += years
}
}
这里在定义的时候要注意,在Groovy
中默认都是public
类型的,还有就是我们在定义属性的时候用基本类型和用对应的包装类型是一样的,这个我们在之前说过
接着我们创建一个脚本来使用下我们定义好的这个类
这里有个地方要和Java
做下区分,在Java
中,我们直接使用类名.属性
,调用的是这个类的成员变量,但是在Groovy
中我们使用类名.属性
相当于调用这个属性对应的get/set
方法
类我们说完了,接下来我们定义一个接口,这个几乎和Java
没有任何区别
interface Action {
void eat()
void drink()
void play()
}
然后让我们的Person
类实现这个接口即可
/**
* 1.Groovy中默认都是public
*/
class Person implements Action {
String name
Integer age
def increaseAge(Integer years) {
this.age += years
}
@Override
void eat() {
}
@Override
void drink() {
}
@Override
void play() {
}
}
在Groovy
中有一个Java
中没有的类型trait
,它其实和我们Java
中的抽象类差不多
trait DefaultAction {
abstract void eat()
void play() {
println ' i can play.'
}
}
使用的方式和接口一样,用implements
关键字,它相当于Java
中的接口和抽象类的结合
Groovy
的元编程
我们首先来看下这个流程图,顺序从上往下,这个流程图适用于方法和属性,我们以方法为例
首选我们调用一个类中的方法,编译器会判断类中是否由此方法,如果是,直接调用我们类中的这个方法,这个和我们的Java
是一样的,但是Java
中却没有否这种情况,在Java
中如果在类中找不到,编译器就会在编译期报错,但是在Groovy
中也会编译通过的,先去MetaClass
中看看有没有这个方法,有的话直接调用.没有的话就看看类中是否重写了methodMissing(String name, Object args)
方法,然后在看看是否重写了类中的invokeMethod(String name, Object args)
方法,都不满足的话才抛出一个异常,接下来我们就来验证下这个流程
我们还是使用我们之前定义的那个Person
类,我们调用一下这个类里面没有的方法
直接报错了,对不对,因为我们这里并不满足上面流程图里面的任何一种情况,接下来我们就对着流程图来分别满足下其中的某一种情况,这里我们从下往上来实现
1、重写invokeMethod
我们在Person
类中重写这个方法
然后我们重新运行下
我们看见没报错,并且打印出来的就是我们刚才重写的invokeMethod
里面的返回值
2.定义methodMissing方法
看下打印结果
我们看见,即使我们把两个方法都定义了一遍,但是打印出来的确是methodMissing
的返回值,说明它的优先级是高于invokeMethod()
的
3、MetaClass
Groovy
中MetaClass
允许在运行时为行为和状态分配行为和状态,而无需编辑原始的源代码。这是Groovy
用来扩展Java JDK
对象的机制,我们可以使用MetaClass
为类在运行时动态添加 属性 和 方法,我们接下来通过几个例子讲解下
3.1 为类动态添加一个属性
3.2 为类动态添加方法
3.3 为类动态添加静态方法
好了,通过上面的例子我们知道Groovy
中的MetaClass
很强大,对于一些第三方的扩展使用MetaClass
居很方便,但是它使用的时候有限制,作用域不是全局,只在当前类中生效,比如我们在A中为Person
动态增加了一个属性sex
,我们想在B中使用是不可行的,我们通过实际例子验证下
我们看见在MetaClassB
中打印Person
的sex
属性就会报错,那这个怎么解决呢?
- 第一种:在
MetaClassB
中再使用MetaClass
动态注入一次属性和方法。 - 第二种: 使用
ExpandoMetaClass.enableGlobally()
方法
第一种没啥好说的,和我们之前讲解的一样即可,我们主要说下第二种方式
首选我们创建一个类Entry
表明我们的应用程序入口
class Entry {
static void main(def args) {
}
}
之后我们创建一个类ApplicationManager
,这个类表示整个应用管理类,我们的初始话工作就在这个类里面
class ApplicationManager {
static void init() {
ExpandoMetaClass.enableGlobally()
//为第三方类添加方法
Person.metaClass.static.createPerson = { String name, int age ->
new Person(name: name, age: age)
}
}
}
我们在init
方法里面完成对Person
的扩展,为其添加一个createPerson
的静态方法,之后我们创建Person
都可以根据这个方法来创建,为了统一我们在建一个Person
管理类
/**
* Person管理类
*/
class PersonManager {
static Person createPerson(String name, int age) {
return Person.createPerson(name, age)
}
}
准备工作都做完了,我们在我们的入口类里面验证下
class Entry {
static void main(def args) {
println '应用程序正在启动...'
ApplicationManager.init()
println '应用程序初始化完成...'
def person = PersonManager.createPerson('Greathfs', 18)
println "the person name is ${person.name} , the person age is ${person.age}"
}
}
我们运行下看看结果
我们看到我们并没有调用我们Person
的构造方法,但是依旧创建成功了,说明这个方法全局都生效