groovy java8_java8 还需要groovy吗?

Groovy开发人员早已熟知Java

8中新引入的概念和新的语言结构了。在Java新版本即将推出的增强特性中,有很多是Groovy在几年前就已经提供了的。从用于函数式编程风格的新语法,到lambdas表达式、collection

streaming和要把方法引用作为一等公民,Groovy开发人员在未来编写Java代码时具有先天性优势。本文将重点关注Groovy和Java

8的共同点,并阐述了Java 8如何解读Groovy中那些熟悉的概念。

我们先来讨论一下函数式编程风格,目前在Groovy中如何使用函数式编程,Java 8的概念如何提供更好的函数式编程风格。

闭包(Closures)也许是Groovy中最好的函数式编程实例了。从内部结构来看,Groovy中的closure只是一个函数式接口实现。函数式接口是指任意只需要实现一个方法的接口。默认情况下,Groovy的closure实现了一个名为“Callable”的函数式接口,实现了这个接口的“call”方法。

def closure = {

"called"

}

assert closure instanceofjava.util.concurrent.Callable

assert closure() == "called"

通过转换closure的类型,我们可以让Groovy实现其他函数式接口。

public interface Function {

def apply();

}

def closure = {

"applied"

} as Function

assert closure instanceof Function

assert closure.apply() == "applied"

在Java 8中很好地引入了闭包和函数式编程的思想。在Java即将发布的版本中函数式接口极为重要,因为在Java

8中针对新引入的Lambda函数式接口提供了隐含的实现。

相关厂商内容

海量服务架构:虽千万人,吾往矣

创业者故事=苟且+诗和远方?

云下的支撑:探索更稳固的架构

你离成为一位合格的技术领导者还有多远?

CNUTCon全球容器技术大会北京站,Get更多亮点,点击了解详情!

相关赞助商

ArchSummit深圳2016将于07月15-16日在华侨城洲际大酒店举行,现价9折抢购,团购报名更多优惠!

我们可以把Lambda函数当成Groovy中的闭包那样去理解和使用。在Java 8中实现callable接口像Groovy中的闭包一样简单。

Callable callable = () -> "called";

assert callable.call() == "called";

你需要特别注意是,Java

8为单行的lambda函数提供了隐含的返回语句,后来Groovy也借鉴了这个概念。将来,Groovy也会为单个抽象方法提供隐含实现(类似于Java

8提供的那些实现)。这个特性使你不必完全派生出closures的具体子类对象就可以使用实例的属性和方法。

abstract class WebFlowScope {

private static final Map scopeMap = [:]

abstractdefgetAttribute(def name);

publicdef put(key, val) {

scopeMap[key] = val

getAttribute(key)

}

protected Map getScope() {

scopeMap

}

}

WebFlowScope closure = { name ->

"edited_${scope[name]}"

}

assert closure instanceofWebFlowScope

assert closure.put("attribute", "val") == "edited_val"

Java

8针对带有接口默认方法的函数式接口提出了一个类似的概念,即Java的新概念“接口默认方法”。他们希望借此概念在不违反接口实现规约(在Java之前的版本中建立的实现规约)的前提下改进核心的API。

当把Lambda函数强制转型为接口时,它们也可以使用接口的默认方法。也就是说在接口中可以内置健壮的API,使开发人员不必改变类型的种类或规约就可以使用这些API。

public interface WebFlowScope {

static final Map scopeMap = new HashMap();

Object getAttribute(Object key);

default public Object put(Object key, Object val) {

scopeMap.put(key, val);

return getAttribute(key);

}

default Map getScope() {

return scopeMap;

}

}

static final WebFlowScope scope = (Object key) ->

"edited_" + scope.getScope().get(key);

assert scope.put("attribute", "val") == "val";

Java

8中的接口默认方法还可以帮我们实现像memoization和trampolining这样的Groovy特性。你可以很简单就实现memoization特性,只需要创建一个带有接口默认方法的函数式接口,并实现这个默认方法让它从缓存中确定估算结果或返回结果就可以了。

public interface MemoizedFunction {

static final Map cache = new HashMap();

R calc(T t);

public default R apply(T t) {

if (!cache.containsKey(t)) {

cache.put(t, calc(t));

}

return (R)cache.get(t);

}

}

static final MemoizedFunction fib

= (Integer n) -> {

if (n == 0 || n == 1) return n;

return fib.apply(n - 1)+fib.apply(n-2);

};

assert fib.apply(20) == 6765;

同样,我们还可以使用Java

8的接口默认方法开发Trampoline的实现。Trampoline是Groovy的一种递归策略,这个特性非常适用于深度递归,而不可能取代Java的调用栈。

interfaceTrampolineFunction {

R apply(T...obj);

public default Object trampoline(T...objs) {

Object result = apply(objs);

if (!(result instanceofTrampolineFunction)) {

return result;

} else {

return this;

}

}

}

// Wrap the call in a TrampolineFunction so that

we can avoid StackOverflowError

static TrampolineFunction

fibTrampoline = (Integer...objs) -> {

Integer n = objs[0];

Integer a = objs.length>= 2 ? objs[1] : 0;

Integer b = objs.length>= 3 ? objs[2] : 1;

if (n == 0) return a;

else return fibTrampoline.trampoline(n-1, b, a+b);

};

除了closures的基本特性以及那些Memoization和Trampolining的高级特性,Groovy还为Collections

API提供了一些有巨大实用价值的语言扩展。我们在使用Groovy时可以充分利用这些扩展点,比如用list 的“each”方法非常简捷地完成写操作。

def list = [1, 2, 3, 4]

list.each { item ->

println item

}

Java

8针对集合的迭代引入了一种与Groovy类似的概念,提供了一个与“each”相似的“forEach”方法,可以用它取代list传统的迭代方式。

List list = new ArrayList<>();

list.add(1);

list.add(2);

list.add(3);

list.add(4);

list.forEach( (Integer item) ->System.out.println(item); );

除了简化list的迭代,Groovy还为应用开发人员提供了各种快捷写法以简化各类list操作。比如“collect”方法,你用这个方法可以将list元素快速映射为新的类型(或新的值),然后把结果放入新的list里。

def list = [1, 2, 3, 4]

defnewList = list.collect { n -> n * 5 }

assert newList == [5, 10, 15, 20]

在Groovy中“collect”的实现比较简单,你只需要把映射当作一个参数传递给“collect”方法。但是,Java

8的实现就稍微有点复杂了,开发人员可以使用Java

8的StreamAPI实现同样的映射和收集策略,实现时要调用“list”的“stream”组件的“map”方法,然后再调用“map”方法返回的“stream”的“collect”方法。开发人员可以这样连续使用Stream

API完成list一连串的操作。

List list = new ArrayList<>();

list.add(1);

list.add(2);

list.add(3);

list.add(4);

ListnewList = list.stream().map((Integer n) -> n * 5).collect(Collectors

.toList());

assert newList.get(0) == 5 &&newList.get(1) == 10

&&newList.get(2) == 15 &&newList.get(3) == 20;

Groovy还能让开发人员使用“findAll”方法简捷地筛选list。

def emails = ['danielpwoods@gmail.com', 'nemnesic@gmail.com',

'daniel.woods@objectpartners.com', 'nemnesic@nemnesic.com']

defgmails = emails.findAll { it.endsWith('@gmail.com') }

assert gmails = ['danielpwoods@gmail.com', 'nemnesic@gmail.com']

同样地,Java 8开发人员可以使用Stream API筛选list。

List emails = new ArrayList<>();

emails.add("danielpwoods@gmail.com");

emails.add("nemnesic@gmail.com");

emails.add("daniel.woods@objectpartners.com");

emails.add("nemnesic@nemnesic.com");

Listgmails = emails.stream().filter(

(String email) ->email.endsWith("@gmail.com") ).collect(Collectors.toList());

assert gmails.get(0) == "danielpwoods@gmail.com"

&&gmails.get(1) == "nemnesic@gmail.com";

Groovy Collections

API扩展还提供了一个“sort”方法,你使用这个方法可以简单地完成对list的排序。“sort”方法还可以接受闭包参数,你可以在闭包中实现所需的特定排序逻辑,闭包会被转为比较器后完成对list的排序。另外,如果只需要对list进行简单地逆序排序,可以调用“reverse”方法反转list的顺序。

def list = [2, 3, 4, 1]

assert list.sort() == [1, 2, 3, 4]

assert list.sort { a, b -> a-b <=> b } == [1, 4, 3, 2]

assert list.reverse() == [2, 3, 4, 1]

再来看Java 8的Stream

API,我们可以使用“sorted”方法对list排序,然后用“toList”方法收集排序结果。“sorted”方法也支持自定义的比较器,它有一个可选的函数式参数(比如Lambda函数),你可以将自定义的比较器作为参数传给方法,就可以很容易地实现特定的排序逻辑和反转list条目的操作了。

List list = new ArrayList<>();

list.add(2);

list.add(3);

list.add(4);

list.add(1);

list = list.stream().sorted().collect(Collectors.toList());

assert list.get(0) == 1 &&list.get(3) == 4;

list = list.stream().sorted((Integer a, Integer b)
->Integer.valueOf(a-

b).compareTo(b)).collect(Collectors.toList());

assert list.get(0) == 1 &&list.get(1) == 4 &&list.
get(2) == 3 &&list.get(3) == 2;

list = list.stream().sorted((Integer a, Integer b)
->b.compareTo(a)).collect

(Collectors.toList());

assert list.get(0) == 2 &&list.get(3) == 1;

如果你试图在一个闭包或Lambda函数内完成所有的处理而连续调用API(比如list

streaming),那么很快就会使代码难以维护。换一个角度来看,如果你要委托相应工作单元特定的方法完成特定的处理,那么这种用法就是一个不错的选择了。

我们使用Groovy时,把方法引用传给函数也可以实现上面所说的目标。你只要使用“.&”操作符去引用方法,就可以把该方法强制转型为闭包传给另一个方法了。由于可以从外部源码引入过程代码,就从本质上提高了实现的灵活性。这样,开发人员就可以在逻辑上组织处理方法,完成更易维护、可持续演进的应用架构了。

def modifier(String item) {

"edited_${item}"

}

def list = ['item1', 'item2', 'item3']

assert list.collect(this.&modifier) == ['edited_item1'

, 'edited_item2', 'edited_item3']

Java 8也为开发人员提供了同样的灵活性,使开发人员可以使用“::”操作符获得方法的引用。

List strings = new ArrayList<>();

strings.add("item1");

strings.add("item2");

strings.add("item3");

strings = strings.stream().map(Helpers::modifier).

collect(Collectors.toList());

assert "edited_item1".equals(strings.get(0));

assert "edited_item2".equals(strings.get(1));

assert "edited_item3".equals(strings.get(2));

你可以把方法引用传给任意以函数式接口为形参的方法。那么,这个方法就会被转型为函数式接口,作为函数式接口执行。

public interface MyFunctionalInterface {

boolean apply();

}

void caller(MyFunctionalInterfacefunctionalInterface) {

assert functionalInterface.apply();

}

booleanmyTrueMethod() {

return true;

}

caller(Streaming::myTrueMethod);

在Java 8里,如果类库开发人员修改了接口规约,那么这些接口的使用者不必为了这些变更去修改那些使用了这个类库的接口。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值