25
Java Programming Best Practices Part IV Timothy Fagan Ishi Systems, Inc.

Java Programming Best Practices Part 4 - Meetup

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Java Programming Best Practices Part 4 - Meetup

Java ProgrammingBest Practices

Part IV

Timothy FaganIshi Systems, Inc.

Page 2: Java Programming Best Practices Part 4 - Meetup

Agenda

Why do we need best practices?The final KeywordExceptions and Exception HandlingSummary

Page 3: Java Programming Best Practices Part 4 - Meetup

Why do we need best practices?Who will read your code?

Compiler – will tell you straight away if your code is invalid. You – when you look at it again in three months. Some other luckless developer – two years from now while you are on vacation - from your new job!

What is worse, buggy code or incomprehensible code?Buggy but comprehensible code can be easily corrected. Correct but incomprehensible code is destined to become buggy as requirements change.

Best practices help us to write maintainable code.Every week I see code like the examples included here - bad code is being written - do not contribute toward it.

Page 4: Java Programming Best Practices Part 4 - Meetup

Use final Wherever Possiblefinal provides important semantic information about code.

Tells readers of the code (including the compiler) a great deal about the author's intent.Java requires an extra keyword be coded to a field, parameter, or local variableModern languages support finals without extra coding, e.g., val or def.

Blank finals have been available since JDK 1.1Declare a local variable as final, but assign it elsewhere before reading from it.They can protect against unnecessary assignments and coding errors.

Using final can help make your code more readable by assigning a value to a meaningfully named variable, and sticking to it.

Page 5: Java Programming Best Practices Part 4 - Meetup

Without final

public Integer getSecondLast(List<Integer> aListOfIntegers) {Integer result = null;

if (aListOfIntegers.size() < 1) {result = null;} else {result = aListOfIntegers.get(aListOfIntegers.size() - 2);}

return result;}

Page 6: Java Programming Best Practices Part 4 - Meetup

With blank final

public Integer getSecondLast(List<Integer> aListOfIntegers) {final Integer result;

if (aListOfIntegers.size() < 1) {result = null;} else {result = aListOfIntegers.get(aListOfIntegers.size() - 2);}

return result;}

Page 7: Java Programming Best Practices Part 4 - Meetup

Without final but with a bug!

public Integer getSecondLast(List<Integer> aListOfIntegers) {Integer result = null;

if (aListOfIntegers.size() < 1) {result = null;} else if (aListOfIntegers.size() > 1) {result = aListOfIntegers.get(aListOfIntegers.size() - 2);}

return result;}

Page 8: Java Programming Best Practices Part 4 - Meetup

With final, initial assignment is not permitted, and so the bug is revealed!public Integer getSecondLast(List<Integer> aListOfIntegers) {final Integer result;

if (aListOfIntegers.size() < 1) {result = null;} else if (aListOfIntegers.size() > 1) {result = aListOfIntegers.get(aListOfIntegers.size() - 2);}

return result;}

Page 9: Java Programming Best Practices Part 4 - Meetup

With long methods, changes to variables may be hard to locatepublic int calculateSomethingComplex(int aNumber) {int result = 0;

if (aNumber != 21) result = -1;

// dozens of lines of code if (aNumber % 3 == 2) { if (aNumber > 17) result = 42; } // dozens of lines of code

return result;}

Page 10: Java Programming Best Practices Part 4 - Meetup

Added final and used �else, but does this compile?public int calculateSomethingComplex(int aNumber){final int result;if (aNumber != 21)result = -1;else {// dozens of lines of codeif (aNumber % 3 == 2) {if (aNumber > 17)result = 42;} else {result = 999;}// dozens of lines of code}return result;}

Page 11: Java Programming Best Practices Part 4 - Meetup

Changing parameter variables is a bad practice.public int calculateSomethingComplex(int aNumber){final int result;if (aNumber != 21)result = -1;else {// dozens of lines of codeaNumber ++;// dozens of lines of coderesult = (aNumber % 3 == 2) ?42 : 999;// dozens of lines of code}return result;}

Page 12: Java Programming Best Practices Part 4 - Meetup

Make all parameters final and add a new meaningfully named local variable.public int calculateSomethingComplex(final int aNumber){final int result;if (aNumber != 21)result = -1;else {// dozens of lines of codefinal int largerNumber = aNumber + 1;// dozens of lines of coderesult = (aNumber % 3 == 2) ?42 : 999;// dozens of lines of code}return result;}

Page 13: Java Programming Best Practices Part 4 - Meetup

Use final Wherever PossibleMake all fields and variables final unless absolutely required not to be final.

Compilers and even CPUs benefit from final fields.Beware that public static final fields are compiled in where possible.Sometimes people forget to handle the "else" condition - final can catch unhandled conditions.

Never assign a value that is never read.E.g., assigning a value and then always assigning another value before ever using it.Misleads the reader into thinking the initial value is significant.final restricts you from doing this with variables that are assigned later.

Page 14: Java Programming Best Practices Part 4 - Meetup

Line numbers in stack traces

public String getFirstName(String aName) { if (aName == null || aName.indexOf(" ") == -1) { throw new IllegalArgumentException(); } return aName.split(" ")[0];}

When null is passed to this method, which line is indicated at the top of the stack trace of the thrown exception?

Page 15: Java Programming Best Practices Part 4 - Meetup

Line numbers in stack traces

public String getFirstName(String aName) { if (aName == null || aName.indexOf(" ") == -1) { RuntimeException e = new IllegalArgumentException(); throw e; } return aName.split(" ")[0];}

What about in this case?

Page 16: Java Programming Best Practices Part 4 - Meetup

Line numbers in stack traces

public String formatName(String aName) { try { return getLastName(aName) + ", " + getFirstName(aName); } catch (IllegalArgumentException e) { e.printStackTrace(); throw e; }}

And finally, which line here?

Page 17: Java Programming Best Practices Part 4 - Meetup

Chain exceptions for additional context

public String formatName(String aName) { try { return getLastName(aName) + ", " + getFirstName(aName); } catch (IllegalArgumentException e) { e.printStackTrace(); throw new RuntimeException(e); }}

And finally, which line here?

Page 18: Java Programming Best Practices Part 4 - Meetup

Line numbers in stack traces

public String getFirstName(String aName) { if (aName == null) { throw new IllegalArgumentException(); } if(aName.indexOf(" ") == -1) { throw new IllegalArgumentException(); } return aName.split(" ")[0];}

When null is passed to this method, which line is indicated at the top of the stack trace of the thrown exception?

Page 19: Java Programming Best Practices Part 4 - Meetup

Always add uniquely identifying information to exceptionspublic String getFirstName(String aName) { if (aName == null) { throw new IllegalArgumentException( "no nulls please"); } if(aName.indexOf(" ") == -1) { throw new IllegalArgumentException( "two names please"); } return aName.split(" ")[0];}

Page 20: Java Programming Best Practices Part 4 - Meetup

Do not initially assign error values

private int getValue(int input) { int value = -1; // what is the meaning of -1 here?

try { // a dozen lines of code value = 17; // a dozen lines of code } catch (Exception e) { logger.log(Level.SEVERE, "unable to get type", e); }

return value;}

Page 21: Java Programming Best Practices Part 4 - Meetup

Assign the value in the catch block

private int getValue(int input) { int value; // no arbitrary value

try { // a dozen lines of code value = 17; // a dozen lines of code } catch (Exception e) { value = -1; logger.log(Level.SEVERE, "unable to get type", e); }

return value;}

Page 22: Java Programming Best Practices Part 4 - Meetup

Another common variation

public String getDataAsUTF8(DatagramPacket datagram) { try { return new String(datagram.getData(),"UTF-8"); } catch (UnsupportedEncodingException e) { logger.log(Level.SEVERE, "unable to get type", e); }

return "";}

Page 23: Java Programming Best Practices Part 4 - Meetup

Another common variation

public String getDataAsUTF8(DatagramPacket datagram) { try { return new String(datagram.getData(),"UTF-8"); } catch (UnsupportedEncodingException e) { logger.log(Level.SEVERE, "unable to get type", e); return ""; }}

Page 24: Java Programming Best Practices Part 4 - Meetup

Exceptions and Exception Handling

The stack trace of an exception is determined on where it is created, not where it is thrown*.

If you catch an exception and rethrow it, wrap it in another exception that chains to the original, otherwise the stack trace will be misleading.

JIT Compiler may hide line numbersMake sure that every exception thrown in a method is unique.

Do not catch an exception unless you have something useful to do:

Retry or alternative path logic.Encapsulating implementation by transforming into a different exception, particularly an unchecked exception.

* fillInStackTrace allows it to be changed, but is not typically a good practice.

Page 25: Java Programming Best Practices Part 4 - Meetup

Summary

Best practices are important.Final is your friendMake coding for exceptions an exception to the ruleAnyone can write complex code - few people can maintain it.