Showing posts with label groovy. Show all posts
Showing posts with label groovy. Show all posts

Neverending Groovy: compareTo() simple as it can be with <=>

Groovy introduced a new operator to compare two values: <=>.

When you use this operator for 2 values a <=> b, Groovy translates this to code something like that a.compareTo(b). Well that's not the true, as it also can handle null values correctly. So you wouldn't get NPE, when you compare value with null or vice versa. For instance, next script:


a = null
b = 12

println "a.compareTo(b) = " + (a <=> b)
println "a.compareTo(a) = " + (a <=> a)
println "b.compareTo(b) = " + (b <=> b)

outputs

a.compareTo(b) = -1
a.compareTo(a) = 0
b.compareTo(b) = 0

where we can see, that null values are handled correctly, without any runtime exceptions.

With such operator we could write really nice and simple code to allow multiple comparisons for object values.

Lets we have a class Person with firstName, lastName and age data:


class Person {
String firstName
String lastName
int age
}


And we need to sort a list of Persons by age, than by name (first by last name, than by first name). Assume, this is standard sorting mode, so we may prefer to implement Comparable interface:



class Person implements Comparable {
String firstName
String lastName
int age

int compareTo(Person other) {
...
}
}


Method compareTo() isn't implemented yet. So lets try to implement in Java first. It would look something like this:


int compareTo(Person other) {
if (other == null) {
return 1;
}

int result = 0;
if (this.age > other.age) {
result = 1;
}
else if (this.age < other.age) {
result = -1;
}
if (result == 0) {
if (this.lastName != null && other.lastName != null) {
result = this.lastName.compareTo(other.lastName);
if (result == 0) {
if (this.firstName != null && other.firstName != null) {
result = this.firstName.compareTo(other.firstName);
}
else {
result = this.firstName != null ? 1 : -1;
}
}
}
else {
result = this.lastName != null ? 1 : -1;
}
}
return result;
}


Hm... Looks ugly! Too much if's, too much checks, too much 0, 1 and -1. Hard to see the logic behind the code ((.

And here how this can be written with Groovy's <=> operator:


int compareTo(Person other) {
age <=> other.age ?: lastName <=> other.lastName ?: firstName <=> other.firstName
}


And a simple test:


def john = new Person(firstName: 'John', lastName: 'Doe', age: 25)
def mark = new Person(firstName: 'Mark', lastName: 'White', age: 25)

print "john <=> mark = " + (john <=> mark)

prints

john <=> mark = -1


As for me, it looks wonderful.

Neverending Groovy: map with default values

More and more Groovy openings for me every day!

Just today, I found that there is a way to setup default value for map entry, if absent. In some cases it may be very handy.

For example, I need to prepare a map with words counts. This simple task should be coded simple too, like:

def map = [:]
words.each { word -> map[word]++ }

But it's not working and fails with NPE exception. Why? Because it's hard to increment null.

Although, there is simple way to avoid this problem even using Groovy API:

def map = [:]
words.each { word -> map[word] = map.get('word', 0) + 1

Here 2nd parameter of map.get() method is the default value, returned if there is no value for key 'word'. And now it's working fine!
Still there is another way, and, as for me, it helps to make code clean and safe. Safe because default logic can be used elsewhere we want to use our map object.

def map = [:].withDefault { 0 }
words.each { word -> map[word]++ }


I bet there is a better way and I'm going to find it...

Neverending Groovy

Вже кілька місяців коджу в основному на Groovy. Прекрасна мова, прекрасна підтримка від IntelliJIdea. Часто знаходжу все нові і кращі спрособи реалізації деколи тривіальних речей.

Для прикладу, необхідно отримати список із значень певного поля кожного елементу списку. Спочатку, я писав доволі простий і зрозумілий код, ясно ще під впливом Java:

List names = []
for (Person person : persons) {
names << person.firstName
}

З часом я удосконалювався і коду ставало менше:

def names = []
persons.each { names << it.firstName }

Виглядає дуже навіть зрозуміло, пишеться швидко і ще швидше читається.
Але і це не був край досконалості, і трохи пізніше я почав писати вже:

def names = persons.collect { it.firstName }

І цей кусок коду поражав мене тим, що був тільки 1 рядок, назва методу collect є зрозумілішою, ніж each у цьому конкретному випадку. І так, може, і писав би й дальше, якби не вирішив спробувати використати для цього spread оператора (*.). І тепер коду стало ще менше:

def names = persons*.firstName

Відмінністю від collect є не тільки простота написання, але і краща швидкодія.

І ось, в процесі написанні цієї статті, я відкрив для себе ще один спосіб. Коли починав писати цю статтю кілька хвилин тому, я вже не вірив, що може бути щось простіше, ніж використання spread оператора для вирішення моєї задачі. Але як виявилося, для отримання значення полів, використовувати spread оператор не є необхідним, і можна написати банально і просто:

def names = persons.firstName


Тож сиджу і думаю, а як можна ще простіше. Боюся зарікатися, що простіше вже бути не може, як вже зрозумів свою помилку кілька хвилин тому.

Groovy цікава мова, з якою все простіше і веселіше. Тож копати в Groovy, Python чи Ruby!

Groovy annotations

As I wrote in my last post Groovy 1.8 is still in progress. One of the new features I liked a lot is annotation support for AST transformations (i.e. @ToString, @Canonical etc.).

As I'm still acquainted with Groovy, I've decided to look what useful annotations it has already. So this post is about what was found.

There are next annotations in the groovy.lang package:

  • @Lazy
  • @Immutable
  • @Newify
  • @Singleton
  • @Mixin
  • @Category

Also want to describe them with few simple samples.

@Lazy

This annotation can be applied to class field. As result new instance for field value is created on get method call for field property. In this case Groovy generates getter method for you, that checks whether field is not initialized yet and initialize it in this case, otherwise returns its value:

@Lazy T x

generates next code:

private T $x

T getX() {
if ($x != null)
return $x
else {
$x = new T()
return $x
}
}

You also can do lazy field initialization with specified value, just assign this value for field with @Lazy annotation:

@Lazy T x = new T(code: 'S')

That will generate next code:

private T $x

T getX() {
T $x_local = $x
if ($x_local != null)
return $x_local
else {
synchronized(this) {
if ($x == null) {
$x = new T(code: 'S')
}
return $x
}
}
}

Absolutely useful thing with only a few of keypresses. That's awesome.

Read more in GroovyDoc

@Immutable

Used to mark classes and make them immutable. That's mean that your class becomes final and nobody can change value field value and you get tuple and map based constructors. Also Groovy generated toString(), hashCode() and equals() methods for you. You should also need to be sure that fields types are immutable, primitives, strings, enums or are annotated with @Immutable.

Read more in GroovyDoc

@Newify

This annotation is perfect for DSL expressions. With it you can change creating new objects expression, for example, use

def b = Book('The Best Book Ever')

instead of

def b = new Book('The Best Book Ever')

Read more in GroovyDoc

@Singleton

Well, another annotation that makes your life easier. As you maybe already understand, this annotation is for types and make them singletons.
getInstance() method is generated for you, so simply call it when you need new instance. If you set annotation lazy parameter to be true, your singleton instance will be created on first call to this method. As for this case method supports double checking locks, you may be sure that only one instance will be created. Here is some sample:

@Singleton
class Player {
def play() {}
def stop() {}
}

...

Player.instance.start()

Read more in GroovyDoc and Singleton transformation.

@Mixin

Used to mix one or few types into annotated type. As result, you can have a class that have members (fields and methods) from other classes, specified in the annotation. And here is sample:

class Book {
String name
String author

def find(text) {
....
}
}

@Mixing(Book)
class HardBookMetadata extends Metadata {
int pages
String publication
}

....
def metadata = new HardBookMetadata()
metadata.title = 'Some cool title'
metadata.author = 'Ruslan Khmelyuk'
metadata.pages = 400

println metadata.find('cool')

As you see, using variable metadata of HardBookMetadata type have access to Book members.
Yeah, this also can be used with @Category.

Read more in GroovyDoc.

Waiting for Groovy 1.8

Groovy 1.8 is going to be released in December 2010. First beta release is already open to download and second beta release will be ready in September.

Groovy developers team preparing for us a few important surprises.

They added more AST transformations, for different cases like @Canonical, @EqualAndHashCash, @ToString and many others. You may use such annotations to generate toString(), equals(), hashCode() methods on compilation. @InheritConstructors helps to generate inherit all parent constructors, and here is link to task.

Another interesting feature is Closure composition, so now it's possible to use special syntax to compose multiple closures, like:

def log = { a -> println(a); a }
def save = { a -> dao.save(a); a}
def notify = { a -> notificationService.notify(a); a }

// so now instead of calling notify(save(log(user)))
// you may call it like
def action = log >> save >> notify
action(user)

// or even like
notify << save << log << user // is that awesome or awful?!

Also they prepared few performance improvements: avoiding calling methods when it's not necessary and processing operations on integers as primary type values but not objects (like, incrementing or adding). As result Groovy can be used for mathematical calculations without performance issues. In this presentation you can read much more about this. And here are related jira tasks so anyone can watch the progress.

This issue is fixed so they can't say that "Ruby is right, Groovy is wrong" anymore :)

Annotations now can accept closure as argument:

@Validator { password.size() > 6 }
String password

This task contains more information and links.

Closure currying is a powerful thing. And now it's possible to curry right and 2nd (of three) parameter. Moreover no need to wait Groovy 1.8 to try this, because it's available right now and right here, starting from version 1.7.2 of course ;)

Well, that's all about this for now. Waiting for Groovy 1.8 and for more interesting things to be released this December.