Good Coding Practices
Week 2 Curriculum
Why Alex?
General Overview:
Commenting, Spacing, and Indentation
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)):
[access modifier] [non-access modifier] [return type] methodName(arguments)
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.
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.
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.
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);
));
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:
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:
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: