KEMBAR78
Effective Java with Groovy | PDF
Effective Java
With Groovy
Naresha K

@naresha_k

https://blog.nareshak.com/
About me
Developer, Coach, Consultant
Founder & Organiser
Bangalore Groovy User Group
Data
Information
Knowledge
Wisdom
https://www.flickr.com/photos/hchalkley/30724738
initial idea was to make a little
dynamic language which compiles
directly to Java classes and provides
all the nice (alleged) productivity
benefits
- James Strachan
http://radio-weblogs.com/0112098/2003/08/29.html
http://commons.wikimedia.org/wiki/File:You-Are-Here-Earth.png
http://upload.wikimedia.org/wikipedia/commons/4/41/Just_ask_%26_You_are_here_-.jpg
Know your guides
Context/ the Problem
What does “Effective Java” say?
The traps
Groovier Solution
Lessons Learned
Tool Wisdom
class Product {
String sku
String description
BigDecimal price
}
Product book1 = new Product(
sku: 'P101',
description: 'Effective Java’,
price: 40.0)
Product book2 = new Product(
sku: 'P101',
description: 'Effective Java’,
price: 40.0)
println book1 == book2
false
Product book1 = new Product(
sku: 'P101',
description: 'Effective Java’,
price: 40.0)
Product book2 = new Product(
sku: 'P101',
description: 'Effective Java’,
price: 40.0)
def stock = [:]
stock[book1] = 100
println stock[book2]
null
#8: Obey the general contract when
overriding equals
#9: Always override hashCode when
you override equals
class Product {
String sku
String description
BigDecimal price
}
class Product {
String sku
String description
BigDecimal price
LocalDate dateOfManufacture
} class Product {
String sku
String description
BigDecimal price
LocalDate dateOfManufacture
LocalDate dateOfExpiry
}
@EqualsAndHashCode
class Product {
String sku
String description
BigDecimal price
}
Product book1 = new Product(
sku: 'P101',
description: 'Effective Java’,
price: 40.0)
Product book2 = new Product(
sku: 'P101',
description: 'Effective Java’,
price: 40.0)
println book1 == book2
true
Product book1 = new Product(
sku: 'P101',
description: 'Effective Java’,
price: 40.0)
Product book2 = new Product(
sku: 'P101',
description: 'Effective Java’,
price: 40.0)
def stock = [:]
stock[book1] = 100
println stock[book2]
100
@EqualsAndHashCode(includes = 'id')
class Product {
Long id
String sku
String description
BigDecimal price
static constraints = {
sku unique: true
}
}
@EqualsAndHashCode(includes = 'id')
class Product {
Long id
String sku
String description
BigDecimal price
static constraints = {
sku unique: true
}
}
@EqualsAndHashCode(includes = ‘sku')
class Product {
Long id
String sku
String description
BigDecimal price
static constraints = {
sku unique: true
}
}
AST Transformation
Single point of representation
of any knowledge
#46: Prefer for-each loops to
traditional for loops
def numbers = [10, 20, 30, 40]
def sum = 0
for(int number in numbers){
sum += number
}
println "Sum: " + sum
numbers.each { number ->
println number
}
println numbers.collect { number ->
number * 2
}
println numbers.inject(0,
{ result, number -> result + number }
)
10
20
30
40
[20, 40, 60, 80]
100
Closures
Favor Internal iterators to
external iterators
Minimise the moving parts
float price = 0.1f;
float total = 0;
for(int i=0; i<10; i++){
total += price;
}
System.out.println("Total: " + total);
Total: 1.0000001
double price = 0.1;
double total = 0;
for(int i=0; i<10; i++){
total += price;
}
System.out.println("Total: " + total);
Total: 0.9999999999999999
#48: Avoid float and double if exact
answers are required
BigDecimal price = new BigDecimal(0.1);
BigDecimal total = new BigDecimal(0);
for(int i=0; i<10; i++){
total = total.add(price);
}
System.out.println("Total: " + total);
Total: 1.0000000000000000555111512312578270211815834045410156250
BigDecimal price = new BigDecimal(0.1);
BigDecimal total = new BigDecimal(0);
for(int i=0; i<10; i++){
total = total.add(price);
}
System.out.println("Total: " + total);
Total: 1.0000000000000000555111512312578270211815834045410156250
def price = 0.1
def total = 0
for(int i=0; i<10; i++){
total += price
}
println "Total: " + total
Total: 1.0
def price = 0.1
def total = 0
for(int i=0; i<10; i++){
total += price
}
println "Total: " + total
println price.class
println total.class
Total: 1.0
class java.math.BigDecimal
class java.math.BigDecimal
Select appropriate default
Principle of Least Astonishment
Bringing in Order
class Person {
String name
int age
}
def geeks = [
new Person(name: 'Raj', age: 30),
new Person(name: 'Leonard', age: 35),
new Person(name: 'Sheldon', age: 32),
new Person(name: 'Sheldon', age: 25),
new Person(name: 'Penny', age: 35)
]
#12: Consider implementing
Comparable
println 10 <=> 20
println 20 <=> 10
println 10 <=> 10
-1
1
0
@ToString
class Person implements Comparable<Person>{
String name
int age
int compareTo(Person other){
name <=> other.name
}
}
geeks.sort(false)
Original:
[Person(Raj, 30),
Person(Leonard, 35),
Person(Sheldon, 32),
Person(Sheldon, 25),
Person(Penny, 35)]
Sorted:
[Person(Leonard, 35),
Person(Penny, 35),
Person(Raj, 30),
Person(Sheldon, 32),
Person(Sheldon, 25)]
geeks.sort(false) { a, b -> a.age <=> b.age}
Original:
[Person(Raj, 30),
Person(Leonard, 35),
Person(Sheldon, 32),
Person(Sheldon, 25),
Person(Penny, 35)]
Sorted:
[Person(Sheldon, 25),
Person(Raj, 30),
Person(Sheldon, 32),
Person(Leonard, 35),
Person(Penny, 35)]
Sort By Age
Sort By Age, Name
int compareTo(Person other) {
if (this.is(other)) {
return 0
}
java.lang.Integer value = 0
value = this.age <=> other.age
if ( value != 0) {
return value
}
value = this.name <=> other.name
if ( value != 0) {
return value
}
return 0
}
geeks.sort(false, { a, b ->
[{it.age}, {it.name}].findResult { c ->
c(a) <=> c(b) ?: null
}
})
Original:
[Person(Raj, 30),
Person(Leonard, 35),
Person(Sheldon, 32),
Person(Sheldon, 25),
Person(Penny, 35)]
Sorted:
[Person(Sheldon, 25),
Person(Raj, 30),
Person(Sheldon, 32),
Person(Leonard, 35),
Person(Penny, 35)]
Sort By Age, Name
@ToString
@Sortable(includes = "age, name")
class Person {
String name
int age
}
Original:
[Person(Raj, 30),
Person(Leonard, 35),
Person(Sheldon, 32),
Person(Sheldon, 25),
Person(Penny, 35)]
Sort By Age, Name
Sorted:
[Person(Sheldon, 25),
Person(Raj, 30),
Person(Sheldon, 32),
Person(Leonard, 35),
Person(Penny, 35)]
Syntactic Sugar:
Spaceship Operator
Simplify common tasks
AST Transformation
Million Dollor Effort
Million Dollor Effort
null check
List<Speaker> getSpeakers(String conference){
null
}
def gr8Speakers = getSpeakers('GR8Conf EU 2019')
if(gr8Speakers != null){
// ...
}
#43: Return empty arrays or
collections, not nulls
println getSpeakers('GR8Conf EU 2019')
.collect { it.firstName }
println getSpeakers('GR8Conf EU 2019')
.findAll { it.firstName.length() > 5 }
println getSpeakers('GR8Conf EU 2019')
.collect { it.firstName } // []
println getSpeakers('GR8Conf EU 2019')
.findAll { it.firstName.length() > 5 } // []
NullObject Pattern
No boilerplate code - $$$
Life is too short for null checks!
#3: Enforce the singleton property
with a private constructor
or an enum type
class Manager {
private static final Manager manager =
new Manager()
private Manager() { super() }
static Manager getInstance() { manager }
}
def m1 = Manager.getInstance()
def m2 = Manager.getInstance()
println m1 == m2
true
class Manager {
private static final Manager manager =
new Manager()
private Manager() { super() }
static Manager getInstance() { manager }
}
def m1 = Manager.getInstance()
def m2 = Manager.getInstance()
println m1 == m2
true
def m3 = new Manager()
println m3 Manager@b968a76
@Singleton
class Manager {}
def m1 = Manager.getInstance()
def m2 = Manager.getInstance()
println m1 == m2
def m3 = new Manager()
println m3
true
Caught: java.lang.RuntimeException: Can't instantiate singleton Manager.
Use Manager.instance
private static Manager manager
static Manager getInstance() {
if(!manager){ manager = new Manager() }
manager
}
private static Manager manager
static synchronized Manager getInstance() {
if(!manager){ manager = new Manager() }
manager
}
private static volatile Manager manager
static synchronized Manager getInstance() {
if(!manager){ manager = new Manager() }
manager
}
@Singleton(lazy=true)
class Manager {}
public static Manager getInstance() {
if (instance != null) {
return instance
} else {
synchronized (Manager) {
if (instance != null) {
return instance
} else {
return instance = new Manager()
}
}
}
}
AST Transformation
YAGNI
Premature optimisation is the root
of all evil
https://www.flickr.com/photos/38080114@N07/8594601982/
#15: Minimize Mutability
Rules to make a class immutable
1. Don’t provide any mutators
2. Ensure that the class can’t be extended
3. Make all fields final
4. Make all fields private
5. Ensure exclusive access to any mutable
components
class ImmutableClass{
private final def field1
private final def field2
//...
private final def field10
public ImmutableClass(f1, f2,… f10){
//initialization
}
}
import groovy.transform.Immutable
@Immutable
class Rectangle{
int length
int breadth
}
def r = new Rectangle(length: 10, breadth: 5)
println r // Rectangle(10, 5)
public Rectangle(java.util.HashMap args) {
if ( args .length == null) {
} else {
this .length = (( args .length) as int)
}
if ( args .breadth == null) {
} else {
this .breadth = (( args .breadth) as int)
}
}
AST Transformation
Readability Matters
Syntactic Sugar
#15: Favour composition over
inheritance
def ph = ['919812312345', '4512341234', ‘19252199916']
as PhoneNumbers
println ph.find { it == '19252199916'}
println ph.findAll { it.endsWith('4') }
def ph = ['919812312345', '4512341234', ‘19252199916']
as PhoneNumbers
println ph.find { it == '19252199916'}
println ph.findAll { it.endsWith('4') }
println ph.indianNumbers()
class PhoneNumbers extends ArrayList {
}
class PhoneNumbers {
private @Delegate List phoneNumbers
PhoneNumbers(numbers) {
phoneNumbers = numbers
}
def indianNumbers() {
phoneNumbers.findAll { it.startsWith('91') }
}
}
trait CanSing {
def sing() {
println "Singing"
}
}
trait CanDance {
def dance() {
println "Dancing"
}
}
class Person implements CanSing, CanDance {}
Person reema = new Person()
reema.sing()
reema.dance()
AST Transformation
Simplify
Take Away
Some of the ‘Effective Java’ already built into
the language
AST Transformations reduce the effort to
implement few more
Effective Java implementations may not always
be effective Groovy implementations (Traps)
Take Away
Programming languages can reduce the
friction to implement good practices
The power of AST transformations is
available to the developers
Thank You

Effective Java with Groovy