前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >20. Groovy 面向对象编程-Traits特性学习-第一篇

20. Groovy 面向对象编程-Traits特性学习-第一篇

作者头像
zinyan.com
发布2022-12-08 17:56:19
5470
发布2022-12-08 17:56:19
举报
文章被收录于专栏:zinyanzinyan

1. 介绍

本篇内容为Groovy学习第二十篇,上一篇学习了关于注解Annotations的相关知识,而这一篇学习Groovy的特性Traits

Traits是面向对象编程中的一种概念,它表示一组可用于扩展类功能的方法。

本节的知识点有点多,将会拆分为多篇内容进行分享。

特性是语言的一种结构构造,它允许:

  • 行为组成。
  • 接口的运行时实现。
  • 行为覆盖。
  • 兼容静态类型检查/编译

等等。

2. trait 关键字

在Groov中,通过trait修饰符定义Traits,它们可以被视为同时携带默认实现和状态的接口

代码语言:javascript
复制
trait ZinyanDemo {      //声明了一个特性对象,ZinyanDemo                     
        String visit() { "访问:https://zinyan.com" }     //定义了一个visit()方法     
}

上面介绍了,定义trait就是一个默认实现和状态的接口,那么我们就可以当接口对象来使用,例如:

代码语言:javascript
复制
class Zinyan implements ZinyanDemo {}          
def b = new Zinyan()    
println(b.visit())  //输出:访问:https://zinyan.com

2.1 声明方法

在Trait中声明方法和类中声明方法是一样的,在上面的示例中就是声明的一个public 公共的方法。

除此外,还可以声明abstract抽象方法:

代码语言:javascript
复制
trait ZinyanDemo {      //声明了一个特性对象,ZinyanDemo                     
        String visit() { "访问:https://zinyan.com" }     //定义了一个公共方法
    abstract String name()   //定义了一个抽象方法
}

我们如果定义了抽象方法,那么在引用该trait的时候就需要实现该抽象方法,例如:

代码语言:javascript
复制
class Person implements ZinyanDemo {                     
    String name() { 'zinyan.com' }      //因为name()是一个抽象方法,所以必须实例化它                       
}

def p = new Person()
println(p.name()) //输出:zinyan.com

PS:到这里,我们可以得到一个结论,trait就和接口一样的使用,但是它相较于接口,可以有实现的方法。而接口中所有方法都必须是抽象的。 在这一点上trait就和抽象类有些相识了,但是我们使用抽象类的时候必须通过extends关键字,而使用trait可以使用implements关键字。

除了抽象类,trait还可以实现private 私有方法。示例:

代码语言:javascript
复制
trait Zinyan {
    //创建一个私有方法
    private String urlInfo() {                      
        'https://zinyan.com'
    }
    String url() {
        def m = urlInfo()                           
        m //返回这个数据值
    }
}
class Demo implements Zinyan {}   //继承之后犹豫没有抽象方法,就可以不用实现任何方法。
def x =new Demo()
println(x.url()) //输出: https://zinyan.com
//而我们强制使用urlInfo方法 就会出现Exception
try {
    assert g.urlInfo()                              
} catch (MissingMethodException e) {
    println "trait中没有找到urlInfo方法"  //输出:trait中没有找到urlInfo方法
}

Trait只支持公共public和私有private方法,不支持protected 受保护的和package private包私有。

我们使用trait主要就是public 和private。当接口使用的。它实现的抽象的还是私有的方法最终都会被编译器加载到继承了trait的实体类中。

如果我们想使用final关键字修饰,避免方法被继承和扩展等情况。建议使用类的形式创建,而不是使用trait创建。

我个人的理解就是,trait是接口的扩展。但并不能取代基类(超类,父类等概念)。

也就是说该用基类进行搭建架构的,大家还是使用基类搭建。

3. this关键字

this表示实现实例。当前实例对象的引用。在trait中使用this,可以把trait当做一个超类。示例如下:

代码语言:javascript
复制
trait Introspector {
    def whoAmI() { this }
}
class Foo implements Introspector {} //定义一个类实现Introspector

def foo = new Foo()

println( foo.whoAmI().is(foo)) //输出: true

因为this就是当前的实例对象。

小结:Trait中可以和Class中一样,使用this关键字,来指代本身。

4. interfaces 关键字

我们通常使用interfaces 关键字用来比对类的类型。例如判断该对象是否属于指定的类。

同时也可以判断对象是否属于某个接口。而trait可以说是接口的一种特殊情况。那么它也当然支持interfaces关键字的判断了。示例如下:

代码语言:javascript
复制
 //创建一个 Named的接口类                         
interface Named {             
    String name()  //定义了一个抽象方法
}
//创建了一个trait对象,它继承了Named接口
trait Greetable implements Named {                      
    String greeting() { "嗨, ${name()} 欢迎光临Z同学小站!" }  //没有实现name方法,而是创建了一个greeting方法
}
//创建一个Person类,继承Greetable 那么就必须重构 抽象方法 name()
class Person implements Greetable {                     
    String name() { 'zinyan' }                             
}

def x = new Person()
println(x.greeting())  //输出:嗨,zinyan 欢迎光临Z同学小站!
println(x instanceof Named) //输出:true
println(x instanceof Greetable) //输出 true

通过上面的示例,可以看到trait实现的类,可以和接口对象一样,通过instanceof 进行判断。

5. 属性

trait不止是可以创建方法,还可以定义属性。

5.1 成员变量

我们通过简单的示例,看看trait的属性定义吧。示例如下:

代码语言:javascript
复制
trait Named {
    String name                             
}
class Person implements Named {} 
//创建一个对象,我们传值饿name就是trait中定义的属性值。

def x = new Person(name:'zinyan.com')
println(x.name) //输出:zinyan.com

通过上面的示例,我们可以看到。通过trait定义的属性,其实就和在类中间定义属性一样的使用。

这也是traitinterface 之间比较大的一个差异点。

前面我们也说过,Trait支持privatepublic访问修饰符。所以,它的属性也可以配置这两种修饰符。示例如下:

代码语言:javascript
复制
trait Counter {
    private int count = 0    //创建一个私有属性值               
    int count() {
        count += 1
        count
    }       
}
//创建一个类,继承Counter对象。
class Foo implements Counter {}             
def f = new Foo()

println(f.count()) //输出: 1
println(f.count()) //输出:2

这与Java8虚拟扩展方法的主要区别在于虚拟扩展方法不携带状态,但特征Trait可以。此外,Groovy中的Trait从Java6开始就受到支持,因为它们的实现不依赖于虚拟扩展方法。这意味着,即使可以从Java类中将trait视为常规接口,该接口也不会有默认方法,只有抽象方法。

5.2 public 变量访问

上面介绍了普通的成员变量的访问,还有一种情况,可以通过__标识符访问。示例如下:

代码语言:javascript
复制
trait Named {
    String name                             
}
class Person implements Named {} 

def x = new Person(name:'zinyan.com')
println(x.Named__name) //输出:zinyan.com

简而言之就是访问trait类型,我们可以将.全部替换为_而最后的变量就是__。例如有一个叫做Zinyan的trait对象。

它所在的包名结构为:com.zinyan.demo 。那么我们在其他地方使用这个对象可以写为: com_zinyan_demo_Zinyan

而如果Zinyan中有一个数据变量为String类型的url。那么就可以写为:def s = com_zinyan_demo_Zinyan__url

PS:虽然说有这种写法,但是Groovy中不建议大家这样使用。所以简单的了解一下相关知识点就可以了。

6.行为组成

trait可以被用来以一种受控的方式实现多重继承。例如我们创建两个不同的trait:

代码语言:javascript
复制
trait FlyingAbility {                           
        String fly() { "我想飞的更高!" }          
}
trait SpeakingAbility {
    String speak() { "我想要大声的怒号!" }
}

如果有一个类implement同时继承了这两个trait对象,示例如下:

代码语言:javascript
复制
class Demo implements FlyingAbility, SpeakingAbility {} 

def zin = new Demo()
println(zin.fly())  //输出:我想飞的更高!
println(zin.speak()) //输出:我想要大声的怒号!

trait鼓励在对象之间重用功能,并通过现有行为的组合来创建新类。

这个特性就和接口对象一样,可以多重组合。

多种trait组合成一个实体类的属性和方法。要比接口和抽象类更灵活。

7. 方法重载

仍然使用上面的示例,我们除了可以继承以外。我们还可以重载继承的方法。示例如下:

代码语言:javascript
复制
//仍然是这两个trait对象
trait FlyingAbility {                           
        String fly() { "我想飞的更高!" }          
}
trait SpeakingAbility {
    String speak() { "我想要大声的怒号!" }
}

class Demo implements FlyingAbility, SpeakingAbility {
     String fly() { "zinyan.com 要飞的更高" }    
     String speak() { "zinyan.com 会分享更多的内容" }
} 

def zin = new Demo()
println(zin.fyl())  //输出:zinyan.com 要飞的更高
println(zin.speak()) //输出:zinyan.com 会分享更多的内容

我们可以直接重载trait中的方法。上面的方法都是public公共的,如果碰见了private的方法会怎么样?示例如下:

代码语言:javascript
复制
//仍然是这两个trait对象
trait FlyingAbility {                           
        String fly() { 
            "我想飞的更高!" 
            }          
}
trait SpeakingAbility {
   private String speak() {
         "我想要大声的怒号!" 
         }
         String getSpeak(){
            "说的是:${speak()}"
         }
}

class Demo implements FlyingAbility, SpeakingAbility {
     String fly() { "zinyan.com 要飞的更高" }    
     String speak() { "zinyan.com 会分享更多的内容" }
} 

def zin = new Demo()
println(zin.fly())  //输出:zinyan.com 要飞的更高
println(zin.speak()) //输出:zinyan.com 会分享更多的内容
println(zin.getSpeak())  //输出:说的是:我想要大声的怒号!

可以看到,private定义的方法,完全不会受到Demo类的影响。保持了独立性。

8. 小结

学到了新的知识点Trait。这个特质在Java中是没有的,java中没有trait关键字。只能创建interface对象。

我们如果将interface的知识代入进来学习,就能很快理解trait的定义和作用了。

本质上来说trait可以说是一个超级接口对象。将接口能实现的场景做了一个很大的扩展。

以上内容可以参考Groovy官网:http://docs.groovy-lang.org/docs/groovy-4.0.6/html/documentation/#_traits 进行学习。

本篇只是介绍了部分trait特性的知识,下一篇继续介绍trait的知识点。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-12-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 zinyan 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 介绍
  • 2. trait 关键字
    • 2.1 声明方法
    • 3. this关键字
    • 4. interfaces 关键字
    • 5. 属性
      • 5.1 成员变量
        • 5.2 public 变量访问
        • 6.行为组成
        • 7. 方法重载
        • 8. 小结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档