def demo = new Demo4()
println demo.foo(“a”)
println demo.bar(“A”, “B”)
}
}
Task :Demo4.main()
unknown method foo(a)
unknown method bar(A,B)
动态处理类的属性
一个有关的题外话
一个传统的
简单JavaBean
,在很多场景下又称为POJO
,大家对此不会陌生,它包含了属性和属性的Getter、Setter并且不包含任意逻辑。
我们知道, POJO需要添加Getter、Setter,哪怕通过IDE生成,并且编译时如果可能,会被inline优化,为此,还有
是否该使用Lombok之争
但是,对于 “应当有Getter、Setter,但是不应当由编写者处理,而是应该由编译器处理” 是多数人认同的
Groovy中对此进行了尝试,提供了 GPath
机制:通过编译器直接生成Getter、Setter,编码时形如属性访问,用"."符 foo.bar
,实际却相对复杂。
kotlin中也有类似的机制。
class GpathDemo {
static class Foo {
String bar
def getBaz() {
return “baz”
}
}
static void main(String[] args) {
Foo foo = new Foo(bar:“bar”)
foo.bar = “bar 2”
println(foo.bar)
println(foo.baz)
}
}
我们可以发现,生成的类:
public static class Foo implements GroovyObject {
private String bar;
@Generated
public Foo() {
CallSite[] var1 = $getCallSiteArray();
super();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
public Object getBaz() {
CallSite[] var1 = $getCallSiteArray();
return “baz”;
}
@Generated
public String getBar() {
return this.bar;
}
@Generated
public void setBar(String var1) {
this.bar = var1;
}
}
public static void main(String… args) {
CallSite[] var1 = $getCallSiteArray();
GpathDemo.Foo foo = (GpathDemo.Foo)ScriptBytecodeAdapter
.castToType(var1[0].callConstructor(GpathDemo.Foo.class,
ScriptBytecodeAdapter.createMap(new Object[]{“bar”, “bar”})),
GpathDemo.Foo.class);
String var3 = “bar 2”;
ScriptBytecodeAdapter.setProperty(var3, (Class)null, foo, (String)“bar”);
var1[1].callStatic(GpathDemo.class, var1[2].callGetProperty(foo));
var1[3].callStatic(GpathDemo.class, var1[4].callGetProperty(foo));
}
读者可能已经注意到了,通过手动添加Getter,也可以利用GPath机制,用"."访问;
另外,读者可能也注意到:设置bar属性时,并未直接访问Setter,此处,我们可以动态的添加属性!
class GpathDemo {
static class Bar {
}
static void main(String[] args) {
Bar.metaClass.“getBaz” = { ->
return “baz”
}
Bar bar = new Bar()
println(bar.baz)
}
}
从编译结果看:
public static class Bar implements GroovyObject {
@Generated
public Bar() {
CallSite[] var1 = $getCallSiteArray();
super();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
}
// main:
public static void main(String… args) {
CallSite[] var1 = $getCallSiteArray();
final class _main_closure1 extends Closure implements GeneratedClosure {
public _main_closure1(Object _outerInstance, Object _thisObject) {
CallSite[] var3 = $getCallSiteArray();
super(_outerInstance, _thisObject);
}
public Object doCall() {
CallSite[] var1 = $getCallSiteArray();
return “baz”;
}
}
_main_closure1 var4 = new _main_closure1(GpathDemo.class, GpathDemo.class);
ScriptBytecodeAdapter.setProperty(var4, (Class)null,
var1[5].callGetProperty(GpathDemo.Bar.class), (String)“getBaz”);
GpathDemo.Bar bar = (GpathDemo.Bar)ScriptBytecodeAdapter.castToType(
var1[6].callConstructor(GpathDemo.Bar.class),
GpathDemo.Bar.class);
var1[7].callStatic(GpathDemo.class, var1[8].callGetProperty(bar));
}
此时,在运行期增加了属性!
如果对Kotlin的扩展和代理比较熟悉,此处应该不难理解
但Groovy的设计更加有趣:
追踪:
-
org.codehaus.groovy.runtime.InvokerHelper#getProperty
-
org.codehaus.groovy.runtime.InvokerHelper#setProperty
发现会进入:GroovyObject
,前面已经接触过
public interface GroovyObject {
Object invokeMethod(String var1, Object var2);
Object getProperty(String var1);
void setProperty(String var1, Object var2);
MetaClass getMetaClass();
void setMetaClass(MetaClass var1);
}
那么借助集合,如 Map
,并复写 getProperty
、 setProperty
,就可以做一些有趣的事情
特殊的Expando类
哈哈,这个有趣的事情Groovy已经做了,这就是 Expando
类。
class ExpandoDemo {
static void main(String[] args) {
Expando expando = new Expando()
expando.foo = “foo”
println(expando.foo)
expando.bar = “bar”
println(expando.bar)
expando.properties.forEach(new BiConsumer() {
@Override
void accept(Object o, Object o2) {
println(“key: o , v a l u e : o,value: o,value:o2”)
}
})
}
}
Task :ExpandoDemo.main()
foo
bar
key:bar,value:bar
key:foo,value:foo
利用ExpandoMetaClass实现Mixin机制
Mixin 即 Mix In,混合, 我们可以笼统地认为:Mixin 即为 在一个类中混入其他类的内容
。
-
对于支持多继承的语言,往往是在讨论
多继承
的问题; -
对于单继承的语言,Java是利用
接口
制造多继承的表现,基于组合
,委托
等方式在目标类中混入
,
从 规格继承
变相解决问题;Ruby 等语言则引入 Minin
从 实现继承
变相解决问题。
我们不再对此概念进行纠缠,可以认为 “多继承语言可以解决很多问题并带来更多的关联问题,单继承语言想要好处又要规避坏处,部分语言提出了Minin机制”
而Groovy的Minin,除了 编译期
要能混入,还要 运行期混入
看个例子,虽然它的场景很不合理,你一定有一万种理由劝说我使用各类设计模式,但不要较真
class MixinDemo {
static class Paint {
def draw(Drawable drawable) {
println(“paint ${drawable.name}”)
}
}
static class Drawable {
String name
}
static void main(String[] args) {
def paint = new Paint()
Drawable.metaClass.draw = paint.&“draw”
def drawable = new Drawable(name: “test”)
drawable.draw(drawable)
}
}
例子中,我们动态的给Drawable添加了draw方法
如果我们将这一过程适当的封装:
class MixinDemo2 {
static class MixinDelegate {
private targetClass
MixinDelegate(targetClass) {
this.targetClass = targetClass
}
def mixin(String asMethodName, Closure closure) {
targetClass.metaClass.“$asMethodName” = closure
}
}
static void main(String[] args) {
def mixin = new MixinDelegate(MixinDemo.Drawable)
mixin.mixin(“draw”,new MixinDemo.Paint().&“draw”)
def drawable = new MixinDemo.Drawable(name: “test”)
drawable.draw(drawable)
}
}
这将会变得很有趣!!!
假设我们有一套 控制协议
,在此之前,我们只能在编译期决定好 指令的执行
– 即控制协议实现,即使运用一些巧妙的设计模式,自由程度也很低, 但现在可以在运行时更为自由地扩展、修改
当然,结合前面的知识,我们可以让它更加的酷炫:
class MixinDemo3 {
static class MixinDsl implements GroovyInterceptable{
private targetClass
MixinDsl(targetClass) {
this.targetClass = targetClass
}
def invokeMethod(String s, o) {
if (s.startsWith(“mixinFun”) && s.length() > 8 && o[0] instanceof Closure) {
def methodName = s[8].toLowerCase() + s[9…-1]
targetClass.metaClass.“$methodName” = o[0]
return null
} else {
println(“cannot handle”)
}
}
}
static void main(String[] args) {
(new MixinDsl(MixinDemo.Drawable)).mixinFunDraw new MixinDemo.Paint().&“draw”
def drawable = new MixinDemo.Drawable(name: “test”)
drawable.draw(drawable)
}
}
此时,添加方法的写法呈现出 DSL的风格
运行时的其他修改
前面我们已经学习了在运行时给类添加方法,接下来再了解更多的内容:
添加构造器
这个例子要和Java进行对比
class RuntimeDemo {
static class Bean {
String a
String b
String c
String d
@Override
public String toString() {
return “Bean{” +
“a='” + a + ‘’’ +
“, b='” + b + ‘’’ +
“, c='” + c + ‘’’ +
“, d='” + d + ‘’’ +
‘}’;
}
}
static class ConstructorDemo {
void main() {
Bean.metaClass.constructor = { String a ->
new Bean(a: a, b: “b”, c: “c”, d: “d”)
}
def bean = new Bean(“a”)
println(bean)
}
}
static void main(String[] args) {
def bean = new Bean(a: “a”, b: “b”, c: “c”)
println(bean)
new ConstructorDemo().main()
}
}
本身Groovy允许我们在构造时设置属性值,但这并不是有重载的构造器!如果没有这个机制,我们就不得不建立一系列重载的构造器,或者老老实实赋值。
但Groovy可以添加构造器
添加静态方法
类比前面提到的添加方法,我们只需要添加关键字 static
就可以添加静态方法。
static void main(String[] args) {
GpathDemo.Foo.metaClass.‘static’.hello = { args1 ->
return “RuntimeDemo:hello,${args1}”
}
println GpathDemo.Foo.hello(“foo”)
}
为对象添加方法
前文已经介绍过给类添加方法,不再赘述。这里注意,我们可以单独给对象添加方法,而不累及该类的其他实例。
static void main(String[] args) {
def bean = new Bean(a: “a”, b: “b”, c: “c”)
//为对象添加方法
try {
bean.hello()
} catch(Exception e) {
println(e.message)
}
def emc = new ExpandoMetaClass(Bean.class, false)
emc.hello = { println “hello” }
emc.initialize()
bean.metaClass = emc
bean.hello()
try {
new Bean().hello()
} catch(Exception e) {
println(e.message)
}
}
很显然,第一次得到Exception,第二次正常打印hello,第三次得到Exception
自省
前文讲了如此之多的运行时修改,很显然,Groovy可以自省,我们简单了解一下以下知识,毕竟这些内容使用不多。
反射
Groovy承袭了Java,那么自然可以使用Java的反射,但是注意:
基于MOP添加的内容,均无法通过Java反射获知
respondsTo 和 hasProperty
class ResponseToDemo {
static class Demo {
def p = “p”
def foo() {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
有任何问题,欢迎广大网友一起来交流,分享高阶Android学习视频资料和面试资料包~
偷偷说一句:群里高手如云,欢迎大家加群和大佬们一起交流讨论啊!
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-nPBB8VqH-1711871976678)]
[外链图片转存中…(img-QOfo3P4m-1711871976680)]
[外链图片转存中…(img-Hx0fm1Qm-1711871976681)]
[外链图片转存中…(img-dPiQ9HSW-1711871976682)]
[外链图片转存中…(img-yBhng9Sr-1711871976683)]
[外链图片转存中…(img-DqSw8WSY-1711871976684)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-ZvB0EYS2-1711871976684)]
最后
有任何问题,欢迎广大网友一起来交流,分享高阶Android学习视频资料和面试资料包~
偷偷说一句:群里高手如云,欢迎大家加群和大佬们一起交流讨论啊!
[外链图片转存中…(img-1osTvbTE-1711871976685)]