1 of 10

Good Coding Practices

​

Week 2 Curriculum

Why Alex?

2 of 10

General Overview:

  • Commenting, Spacing, and Indentation
  • Modifiers
  • Construction Patterns (singleton, helper, factory, dependency injection)
  • Interfaces and Generics
  • Anonymous Methods
  • Inheritance and Encapsulation
  • Exceptions

​

3 of 10

Commenting, Spacing, and Indentation

  • Commenting
    • Especially when collaborating with others, readability is key to maintaining productivity, so annotate your code whenever you believe its behavior or syntax may be unclear (typically every 5-15 lines).
    • In Java, single-line comments are written as // this is a comment, and multi-line comments are written as /* this is a comment */
    • You can also comment out code blocks that are momentarily unnecessary, but when you’re running your code (e.g., at an FRC event), duplicate that project and delete the comments to reduce run time.
  • Spacing
    • Make sure you always include spaces between methods, loops, conditionals, etc., and their parameters and conditions, respectively, classes/interfaces/enums and brackets, etc., to keep your code readable.
  • Indentation
    • Indent methods 1 line in with respect to their enclosing class, conditionals, exceptions, etc., 1 line in with respect to their enclosing method, and to avoid long lines of method chains (with multiple commands inside each method), indent 1 line for each exterior method.

4 of 10

Modifiers

Accessibility

Within package

Subclasses

Everything else

Public

Yes

Yes

Yes

Protected

Yes

Yes

No

Private

Yes

No

No

No modifier (default)

No

No

No

​

Description

Static

Only one instance of member exists at a time, can be accessed without reference to any object.

Final

Member’s definition cannot be changed (e.g., a subclass of a class with final methods cannot overwrite those methods; final classes cannot be instantiated).

Abstract

Members with this declaration don’t have a defined implementation, and can be overwritten.

Synchronized

When multiple processes are being executed concurrently, synchronization prevents data access by multiple of the threads being executed (we probably won’t be using this very often).

Method signature (return type + argument(s)):

  1. A return type is what the method at hand outputs upon being called. Possible return types include void and primitives such as int, double, long, string, bool, etc.
  2. Arguments are the parameters you are required to include in a method declaration (e.g., methodName(string stringName))

[access modifier] [non-access modifier] [return type] methodName(arguments)

5 of 10

Construction Patterns

Note that although the following construction patterns are very generalized, they are widely extensible to practically all of the Java you will be writing this season.

Singleton

Helper

Only one instance of a singleton class can be created at a given time. Every time getInstance() is called, if an instance of the class doesn’t exist, one will be created. Synchronized modifier is only needed if your program is multithreaded (won’t usually be the case).

Should be non-instantiable, and can only contain static methods. These classes often contain methods that don’t fit within any other class very well (you can think of these classes as libraries), such as basic functions that are tedious to reimplement multiple times.

6 of 10

Construction Patterns cont.

Factory

Dependency Injection

An FRC example of PID feedforward augmentation

Because this class has all of its dependencies provided externally, it is easier to modify and test, and the class is independent from the instantiation of the objects it depends on (if we had taken the getInstance() route, a PID controller would created regardless of whether we actually needed it).

One way to implement factory pattern.

Recommended implementation (more abstract and extensible).

Create a modified method using @Override annotation. Note that the generic newFactory() method is hidden behind the implementation of the FactoryInterface interface.

7 of 10

Interfaces and Generics

Abstraction is a very important organizational tool that is especially useful when our code becomes more complex (aka when it becomes dependency spaghetti). Interfaces are used for abstraction over methods (or classes or more interfaces), whereas generics are used for abstraction over types.

An interface is generally a set of methods without bodies (you provide the implementation by overriding the respective methods within the class that extends the given interface). In addition to methods, you can nest interfaces within interfaces, and include classes and enums. You can only provide an implementation for a method within an interface if you include the default keyword in its method signature.

Having problems with type safety? Too many unchecked casts? *cough, cough* Alex *cough, cough* Meet generics.

Instead of defining certain types in your class or interface, represent them by an uppercase letter. You can put the <[generics here]> after the last modifier in a method signature, and after its name in a class or interface signature.

8 of 10

Anonymous Methods

ft. the infamous lambda… A functional interface that condenses your code while making it slightly more unreadable (nameless + classless)

Simply put, a lambda expression can be written as:

(parameters) -> {thing you want to execute/body}

​

You can define objects as lambda expressions or pass the expression itself as a parameter to a method. Use the curly braces if the expression body is multiple lines long.

When we’re writing anonymous methods, we’ll either use the lambda (->) or the method reference (::) operator.

The method reference operator can be used to reference static methods, instance methods of an instantiated object or an arbitrary object of a defined type, and can reference a constructor

Generalized use cases for this operator:

Class::staticMethod

Instance::instanceMethodName

Type::methodName

Class::new

buttonX.onButtonPressed( () -> armClassInstance.armUp());

buttonX.onButtonPressed(armClassInstance::armUp);

Two ways to define the same method (moves an arm up when a button is pressed):

You can think of method references as a subset of lambdas (all method references can be written as lambdas, but not vice versa). Prioritize clarity, and use method references when you need to call a section of code from multiple different places in your program. The example below (creates a new instance of a functional drive command), demonstrates the difference between these conventions.

new functionalDriveCommand(() -> drivetrainInstance.drivetrainType(

driveController::getY, driveController::getX, drivetrainInstance::resetEncoders, drivetrainInstance);

));

9 of 10

Inheritance and Encapsulation

We already mentioned inheritance in the construction patterns section (factory pattern), but let’s extend our knowledge to more general class hierarchies.

Components of Inheritance:

  1. Use extends keyword to demonstrate that a class is a subclass of its parent class
  2. Must use @Override annotation on each method of from the parent class, unless the methods in the parent class were declared as final
  3. Use super() keyword to reference objects from the parent class (can call variables, methods, and constructors–generally only called if there are arguments in the parent’s constructor)
  4. Although multiple inheritance is not allowed, the implements keyword can be used in the class signature instead with multiple classes (you can also have a scheme where A implements B, C extends A, implements D, and E extends C, implements F, etc.)
  5. Try to use interfaces as parents rather than abstract or regular classes, so you code is easier to modify and maintain.

Encapsulation was also partially covered in the modifiers section, but explicitly speaking, it refers to restricting the visibility of implementation details to make code easier to modify (fewer unnecessary dependencies, etc.). The key takeaways are:

​

  1. Use getters and setters to never directly expose a class’ fields
  2. Minimize using the public keyword whenever you can (instead use private or protected)

10 of 10

Exceptions

Exceptions are used to signal abnormal or potentially abnormal program behavior. We can use checked exceptions (denoted by the throws keyword in a method signature or wrapped in a try-catch statement) to log code errors and keep the compiler from stopping all processes after reaching a non-fatal condition (not recommended but sometimes convenient).

Exception is the most generic type of exception. Other common exceptions are IOException, NullPointerException, and IllegalArgumentException. Here’s a comprehensive list of exceptions: https://programming.guide/java/list-of-java-exceptions.html.

​

Lastly, try-with-resources statements are used to reduce clutter when you’re, for example, closing a file within a try-catch statement. Instead of dealing with nested exceptions within the finally statement, you can have arguments within your try statement: