TIMTOWTDI and Java

(and not only Java!)

Grzegorz Rożniecki (@xaerxess)

About me

Java / Perl / JavaScript developer

Software reader / writer

xaerxess.pl

@xaerxess

Xaerxess

About this talk

Slides | Source on GitHub

Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.

TIMTOWTDI

There is more than one way to do it

Tim Toady

Programming Perl
Programming Perl
Programming Perl TIMTOWTDI
"The very fact that it's possible to write messy programs in Perl is also what makes it possible to write programs that are cleaner in Perl than they could ever be in a language that attempts to enforce cleanliness. The potential for greater good goes right along with the potential for greater evil."
- Larry Wall, Perl creator

Why?

Python Zen

There should be one
- and preferably only one -
obvious way to do it

Flexible cat
Jeremy: Powaaah!

Tim Toady in practice

What's goalkeeper for? World Cup 2014 - Michael Neuer

Tim Toady in practice

What's goalkeeper for? World Cup 2014 - Michael Neuer
"There are only two kinds of languages: the ones people complain about and the ones nobody uses"
- Bjarne Stroustrup, C++ creator
"There are only 10 kinds of languages: the ones people complain about and the ones nobody uses"
- Bjarne Stroustrup, C++ creator
Programming joke fail

Perl doing for loop


for (my $i = 0; $i <= 3; ++$i) {
    print $i;
}
            

Perl doing for loop


for my $i (0..3) {
    print $i;
}
            

Perl doing for loop


for (0..3) {
    print $_;
}
            

Perl doing for loop


for (0..3) {
    print;
}
            

Perl doing for loop


print for (0..3);
            

Linguistic relativity

vs

Universal grammar

Sapir–Whorf hypothesis

Edward Sapir Benjamin Whorf
Edward Sapir Benjamin Lee Whorf
linguistics theory Hopi - time & space
Congnitive linguistics
Perl Java
created in 1987 created in 1991
Perl 5 released in 1994 JDK 1.0 released in 1995
"get things done" "write once, run anywhere"
OO support in Perl 5 OO support from the beginning

TIMTOWTDI

BSCINABTE

There’s more than one way to do it
but sometimes consistency is not a bad thing either

Tim Toady

Bicarbonate

OOP in Perl


package Foo;

sub new {
  my $class = shift;
  my $self = { }; # or [ ] or any arbitrary scalar value
  return bless $self, $class;
}

1;
            
OOP in Perl - WAT
OOP in Perl - Moose

Standards

People like rules and guidelines. They like to agree with them, or to argue with them.
Tabs vs Spaces
Emacs vs Vim

Inconsistency

Inconsistency

TIMTOWTDI?

Java and TIMTOWTDI

finally...

  • simple syntax
  • stable and battle tested
  • JVM
  • has coding standards
  • has best practices

but...

  • verbose syntax
  • backwards compatibility
  • JVM languages are not Java
  • has many coding standards
  • has some best practices

Java syntax


final class Person {

  private final String name;
  private final Integer age;

  public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return name;
  }

  public Integer getAge() {
    return age;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((age == null) ? 0 : age.hashCode());
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    Person other = (Person) obj;
    if (age == null) {
      if (other.age != null)
        return false;
    } else if (!age.equals(other.age))
      return false;
    if (name == null) {
      if (other.name != null)
        return false;
    } else if (!name.equals(other.name))
      return false;
    return true;
  }

  @Override
  public String toString() {
    return "Person [name=" + name + ", age=" + age + "]";
  }

}
            
vs

case class Person(name: String, age: Int)
            

Java 7+: Objects
Java < 7 - Guava: Objects


  @Override
  public int hashCode() {
    return Objects.hash(name, age);
  }

  @Override
  public boolean equals(final Object obj) {
    if (!(obj instanceof Person))
      return false;
    final Person other = (Person) obj;
    return Objects.equals(this.name, other.name)
        && Objects.equals(this.age, other.age);
  }
            

Better?

Guava 18+: MoreObjects.toStringHelper
Guava < 18: Objects.toStringHelper


  @Override
  public String toString() {
    return MoreObjects.toStringHelper(this)
        .add("name", name)
        .add("age", age)
        .toString();
  }
            

Also better?


  @Override
  public String toString() {
    return "Person [name=" + name + ", age=" + age + "]";
  }
            

Lombok: @Value


@Value public class Person {
  String name;
  Integer age;
}
            

Lombok: black magic

Black magic

Google AutoValue: @AutoValue


@AutoValue
abstract class Person {

  public static Person create(String name, Integer age) {
    return new AutoValue_Person(name, age);
  }

  public abstract String getName();
  public abstract int getAge();
}
            

Good trade-off?

Legacy

You should prefer List<Foo> over Foo[] whenever possible.

  • A collection can be mutable or immutable. A nonempty array must always be mutable.
  • A collection can be thread-safe; even concurrent. An array is never safe to publish to multiple threads.
  • A collection can allow or disallow null elements. An array must always permit null elements.
  • A collection is type-safe; an array is not. Because arrays "fake" covariance, ArrayStoreException can result at runtime.
  • A collection can hold a non-reifiable type (e.g. List<Class<? extends E>> or List<Optional<T>>). With an array you get compilation warnings and confusing runtime exceptions.
  • A collection has a fully fleshed-out API; an array has only set-at-index, get-at-index and length.
  • A collection can have views (unmodifiable, subList, filter...). No such luck for an array.
  • A list or set's equals, hashCode and toString methods do what users expect; those methods on an array do anything but what you expect -- a common source of bugs.
  • Because of all the reasons above, third-party libraries like Guava won't bother adding much additional support for arrays, focusing only on collections, so there is a network effect.

So, why would you ever use object arrays?

  • have to interact with an API that uses them, and you can't fix that API
    • convert to/from a List as close to that API as you can
  • have a reliable benchmark that shows you're actually getting better performance with them
    • benchmarks can lie, and often do
  • can't think of any other reasons

JVM languages

  • JVM is great!
  • Scala, Clojure, Jython, JRuby
  • ...but employers don't trust them...
  • ...and there's big need for Java devs, not JVM-based devs
  • :-(

Coding standards

Be consistent in your team.

Ex. consistent IDE preferences

The "final" battle

Final battle

Best practices

- Always override hashCode when you override equals - Minimize mutability - Design and document for inheritance or else prohibit it - Prefer lists to arrays - Know and use the libraries _Sounds familiar?_
Effective Java

Lambdas

"Closures feel to me like one of these design Black Holes. (...) So, should we just give in to the force of gravity and go the rest of the way? The debate's afoot. It's going to be an entertaining year."
- James Gosling, Java creator - in 2006, on his blog
"Java has closures, it's just called 'anonymous inner classes' (...). [They] are the better and more 'java-way' to do closures. Adding an additional syntax for closures will only make the language more complex without solving even one real problem."

...and in 2014 (finally) instead of this:


List<Album> favs = new ArrayList<>();
for (Album a : albums) {
    boolean hasFavorite = false;
    for (Track t : a.tracks) {
        if (t.rating >= 4) {
            hasFavorite = true;
            break;
        }
    }
    if (hasFavorite) {
        favs.add(a);
    }
}
Collections.sort(favs, new Comparator<Album>() {
    @Override public int compare(Album a1, Album a2) {
        return a1.name.compareTo(a2.name);
}});
            

...you can do this:


List<Album> sortedFavs =
  albums.stream()
        .filter(a -> a.tracks.anyMatch(t -> (t.rating >= 4)))
        .sorted(Comparator.comparing(a -> a.name))
        .collect(Collectors.toList());
            

There is more than one way to do it, finally!

Lambdas

THE END

Questions?

Please send me a feedback:
@xaerxess or xaerxess at gmail dot com