Effective Java
With Groovy -
How Language
Influences Adoption of
Good Practices
Naresha K
@naresha_k
https://blog.nareshak.com/
APACHECON @HOME
Spt, 29th – Oct. 1st 2020
About me
Developer, Architect &
Tech Excellence Coach
Founder & Organiser
Bangalore Groovy User
Group
3
Java Language
JVM
Platform/ Real Machine Platform/ Real Machine Platform/ Real Machine
4
Java Language
Platform/ Real Machine Platform/ Real Machine Platform/ Real Machine
Groovy
JVM
5
Most people talk about Java the language,
and this may sound odd coming from me,
but I could hardly care less. At the core of
the Java ecosystem is the JVM.
- James Gosling,
Creator of the Java Programming Language(2011, TheServerSide)
6
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
Know your guides
Context/ the Problem
What does “Effective Java” say?
The traps
Groovier Solution
Lessons Learned
Tool Wisdom
9
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
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
Billion Dollor Effort
Billion Dollor Effort
null check
40
List<Speaker> getSpeakers(String conference) {
return null;
}
List<Speaker> speakers = getSpeakers(“ApacheCon@Home 2020“);
if (speakers != null) {
//...
}
#43: Return empty arrays or
collections, not nulls
println getSpeakers(‘ApacheCon@Home 2020')
.collect { it.firstName }
println getSpeakers(‘ApacheCon@Home 2020’)
.findAll { it.firstName.length() > 5 }
println getSpeakers(‘ApacheCon@Home 2020')
.collect { it.firstName } // []
println getSpeakers(‘ApacheCon@Home 2020’)
.findAll { it.firstName.length() > 5 } // []
NullObject Pattern
No boilerplate code - $$$
Life is too short for null checks!
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') }
}
}
Favoring Composition
- The Groovy Way
APACHECON @HOME
Spt, 29th – Oct. 1st 2020
AST Transformation
Simplify
#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
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
Take Away
The power of AST transformations is
available to the developers
https://blog.nareshak.com/effective-java-with-groovy/
Thank You
APACHECON @HOME
Spt, 29th – Oct. 1st 2020

Effective Java with Groovy - How Language Influences Adoption of Good Practices

  • 1.
    Effective Java With Groovy- How Language Influences Adoption of Good Practices Naresha K @naresha_k https://blog.nareshak.com/ APACHECON @HOME Spt, 29th – Oct. 1st 2020
  • 2.
    About me Developer, Architect& Tech Excellence Coach Founder & Organiser Bangalore Groovy User Group
  • 3.
    3 Java Language JVM Platform/ RealMachine Platform/ Real Machine Platform/ Real Machine
  • 4.
    4 Java Language Platform/ RealMachine Platform/ Real Machine Platform/ Real Machine Groovy JVM
  • 5.
    5 Most people talkabout Java the language, and this may sound odd coming from me, but I could hardly care less. At the core of the Java ecosystem is the JVM. - James Gosling, Creator of the Java Programming Language(2011, TheServerSide)
  • 6.
  • 8.
    initial idea wasto 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
  • 9.
    Know your guides Context/the Problem What does “Effective Java” say? The traps Groovier Solution Lessons Learned Tool Wisdom 9
  • 11.
    float price =0.1f; float total = 0; for(int i=0; i<10; i++){ total += price; } System.out.println("Total: " + total); Total: 1.0000001
  • 12.
    double price =0.1; double total = 0; for(int i=0; i<10; i++){ total += price; } System.out.println("Total: " + total); Total: 0.9999999999999999
  • 13.
    #48: Avoid floatand double if exact answers are required
  • 14.
    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
  • 15.
    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
  • 16.
    def price =0.1 def total = 0 for(int i=0; i<10; i++){ total += price } println "Total: " + total Total: 1.0
  • 17.
    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
  • 18.
  • 19.
    class Product { Stringsku String description BigDecimal price }
  • 20.
    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
  • 21.
    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
  • 23.
    #8: Obey thegeneral contract when overriding equals #9: Always override hashCode when you override equals
  • 25.
    class Product { Stringsku 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 }
  • 26.
    @EqualsAndHashCode class Product { Stringsku String description BigDecimal price }
  • 27.
    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
  • 28.
    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
  • 29.
    @EqualsAndHashCode(includes = 'id') classProduct { Long id String sku String description BigDecimal price static constraints = { sku unique: true } }
  • 30.
    @EqualsAndHashCode(includes = 'id') classProduct { Long id String sku String description BigDecimal price static constraints = { sku unique: true } }
  • 31.
    @EqualsAndHashCode(includes = ‘sku') classProduct { Long id String sku String description BigDecimal price static constraints = { sku unique: true } }
  • 32.
    AST Transformation Single pointof representation of any knowledge
  • 34.
    #46: Prefer for-eachloops to traditional for loops
  • 35.
    def numbers =[10, 20, 30, 40] def sum = 0 for(int number in numbers){ sum += number } println "Sum: " + sum
  • 36.
    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
  • 37.
    Closures Favor Internal iteratorsto external iterators Minimise the moving parts
  • 38.
  • 39.
  • 40.
    40 List<Speaker> getSpeakers(String conference){ return null; } List<Speaker> speakers = getSpeakers(“ApacheCon@Home 2020“); if (speakers != null) { //... }
  • 41.
    #43: Return emptyarrays or collections, not nulls
  • 42.
    println getSpeakers(‘ApacheCon@Home 2020') .collect{ it.firstName } println getSpeakers(‘ApacheCon@Home 2020’) .findAll { it.firstName.length() > 5 }
  • 43.
    println getSpeakers(‘ApacheCon@Home 2020') .collect{ it.firstName } // [] println getSpeakers(‘ApacheCon@Home 2020’) .findAll { it.firstName.length() > 5 } // []
  • 45.
    NullObject Pattern No boilerplatecode - $$$ Life is too short for null checks!
  • 46.
  • 47.
  • 48.
    Rules to makea 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
  • 49.
    class ImmutableClass{ private finaldef field1 private final def field2 //... private final def field10 public ImmutableClass(f1, f2,… f10){ //initialization } }
  • 50.
    import groovy.transform.Immutable @Immutable class Rectangle{ intlength int breadth } def r = new Rectangle(length: 10, breadth: 5) println r // Rectangle(10, 5)
  • 51.
    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) } }
  • 52.
  • 54.
    #15: Favour compositionover inheritance
  • 55.
    def ph =['919812312345', '4512341234', ‘19252199916'] as PhoneNumbers println ph.find { it == '19252199916'} println ph.findAll { it.endsWith('4') }
  • 56.
    def ph =['919812312345', '4512341234', ‘19252199916'] as PhoneNumbers println ph.find { it == '19252199916'} println ph.findAll { it.endsWith('4') } println ph.indianNumbers()
  • 57.
  • 58.
    class PhoneNumbers { private@Delegate List phoneNumbers PhoneNumbers(numbers) { phoneNumbers = numbers } def indianNumbers() { phoneNumbers.findAll { it.startsWith('91') } } }
  • 59.
    Favoring Composition - TheGroovy Way APACHECON @HOME Spt, 29th – Oct. 1st 2020
  • 60.
  • 62.
    #3: Enforce thesingleton property with a private constructor or an enum type
  • 63.
    class Manager { privatestatic final Manager manager = new Manager() private Manager() { super() } static Manager getInstance() { manager } } def m1 = Manager.getInstance() def m2 = Manager.getInstance() println m1 == m2 true
  • 64.
    class Manager { privatestatic 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
  • 65.
    @Singleton class Manager {} defm1 = 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
  • 66.
    private static Managermanager static Manager getInstance() { if(!manager){ manager = new Manager() } manager }
  • 67.
    private static Managermanager static synchronized Manager getInstance() { if(!manager){ manager = new Manager() } manager }
  • 68.
    private static volatileManager manager static synchronized Manager getInstance() { if(!manager){ manager = new Manager() } manager }
  • 69.
    @Singleton(lazy=true) class Manager {} publicstatic Manager getInstance() { if (instance != null) { return instance } else { synchronized (Manager) { if (instance != null) { return instance } else { return instance = new Manager() } } } }
  • 70.
  • 71.
    Take Away Some ofthe ‘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)
  • 72.
    Take Away Programming languagescan reduce the friction to implement good practices
  • 73.
    Take Away The powerof AST transformations is available to the developers
  • 74.
  • 75.
    Thank You APACHECON @HOME Spt,29th – Oct. 1st 2020