Java is a class-based, object-oriented programming language. This object-oriented paradigm was developed in the late 1950s. It grew out of a need to reliably build larger, more complex systems than was previously possible using an imperative programming model (you can read more at https://en.wikipedia.org/wiki/Imperative_programming). Object orientation's original computation model focused on data encapsulation and object interaction through message passing versus imperative's model of statements that change the state of a program. These objects can be derived from classes, prototypes, or through some other mechanism. Object-oriented programming was first popularized in languages, such as Simula (you can read more at https://en.wikipedia.org/wiki/Simula) and Smalltalk (you can read more at https://en.wikipedia.org/wiki/Smalltalk). There are now many major languages that use this programming model, including Java.
It has statically and dynamically typed program elements. This means that you can inspect and dispatch (decide) based on the type of object at runtime. Java variable types are also checked at compile time. Let's take a look at a possible implementation of our simple-moving-average
function using Java:
import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.nio.file.Files; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class Lagging { public Stream<Double> readPrices(String path) throws URISyntaxException , IOException { return Files.lines(Paths.get(new URI(path)), StandardCharsets.UTF_8) .map(Double::parseDouble); } public List listPrices(String path) throws URISyntaxException , IOException { return this.readPrices(path).collect(Collectors.toList()); } public List simpleMovingAverages(String pricesPath, int tickWindow) throws URISyntaxException , IOException { List priceList = this.listPrices(pricesPath); List averageList = new ArrayList(); int begin = 1; int end = tickWindow; while(end <= priceList.size()) { List currentWindow = priceList.subList(begin, end); Iterator iter = currentWindow.iterator(); double total = 0d; while (iter.hasNext()) { total += (Double)iter.next(); } averageList.add(total / tickWindow); begin++; end++; } return averageList; } public static void main(String[] args) throws URISyntaxException , IOException { String pricesPath = "file:///Users/timothyw/Projects/LaggingJava/src/main/resources/prices.txt"; int tickWindow = 20; Lagging lagging = new Lagging(); List simpleMovingAverages = lagging.simpleMovingAverages(pricesPath, tickWindow); System.out.println(simpleMovingAverages); } }
The main
method is the start point of the program. However, before we study it, notice that it exists inside the Lagging
class. All program state and behavior must be created inside a class or similar structure (interface, abstract class, and so on). The idea is that the class is a template from which we create an actual thing called an object.
Static program elements are those that exist in the class or template itself, not its objects. This is the condition of the main
method. After compiling this code, there will be a compiled Lagging
representation that we can run from our computer. This is done by invoking the java
runner (or runtime environment) and passing in the compiled Lagging
representation. By default, the java
runtime invokes the static main method on the class it is given. In this case, when run, main
creates a string variable that represents the input file on a computer's hard drive. The Lagging lagging = new Lagging();
expression then creates a new Lagging
instance or an object. The next expression, List simpleMovingAverages = lagging.simpleMovingAverages(pricesPath, tickWindow);
, calls the simpleMovingAverages
method on this object. The result is assigned to another object of the abstract List
, type which is then printed to the console.
Just by studying the main method and its context, we see that classes and class instantiation is central to where Java places state and behavior. Diving into the simpleMovingAverages
method, the first expression calls the listPrices
method on the current object or the this
reference. listPrices
then calls out again to the readPrices
helper method to pull in the pricelist.txt
data file, divides it into a list of text lines, and then converts each element of this list into a numeric value of the java.lang.Double
type.
The second expression, List averageList = new ArrayList();
, simply creates a list that will be added to or mutated later on in the method. The next two expressions set up the begin
and end
states to be used to control the while
loop. while
loops are control structures in imperative and object-oriented languages that repeatedly execute code inside a block until some condition is met. In our case, we have two while loops. The first while(end <= priceList.size())
condition will execute everything inside its block, while the end
variable is below or equal to the length (or size) of the price list. What this block is doing is taking a subset or window of the entire list and calculating the average for this window. The second while loop walks or iterates over the subset or currentWindow
, using the Iterator type in order to sum all the numbers in this subset. Each time the outer while
loop is executed, the average is calculated, the result placed into averageList
, and the begin
and end
markers each increment forward until end
hits the length of the list. When we run this class, the console printed averageList
, has the average values we expect.
[6.971467579790411, 7.464540409139185, 8.010389878709972, 8.595550217357273, 9.199880686839014, 9.803134294595337, 10.391281772750983, 10.960810205584073, 11.519789847777256, 12.085410173832697, 12.657204984470962, 13.512979176946038, 14.336798333665858, 15.033597887711505, 15.93293648884989, 17.236687274535527, 18.017166796811743, 18.747178634896592, 19.36891681775512, 19.88107990363371, 20.352063482865127, 20.809990525663345, 21.292779590969797, 21.65720104327843, 21.88759572553658, 22.0960538285178, 22.522518975747712, 22.921023864595572, 23.300273299533917, 23.677893061861866, 23.791812046695554, 23.92261161858174, 24.13540925343312, 24.28043376993932, 23.99307801388237, 23.69529121331615, 23.444825362117815, 23.313832222382498, 23.30398859426755, 23.34531490845263, 23.386641222637703, 23.52694480589677, 23.67574694970999, 23.906119057816472, 24.25121614021084, 24.211544668391998, 24.23599987692504, 24.305644963968547, 24.243653669023793, 24.31898689789535, 24.369948081810513, 24.364340164808066, 24.518113870232213, 24.573545352458943, 24.63940787919494, 24.75959935515974, 24.806090418384898, 24.772532158349563, 24.774246735296167, 24.857257750991796, 24.765966922838224, 24.67207478924459, 24.61362131993375, 24.52537925344091, 24.58801417608733, 24.586522418381865, 24.532091262456568, 24.56313935471497, 24.393672293029404, 24.188468574045014, 24.05833078712983, 23.919361348321903, 23.947386025442054, 23.872246343092222, 23.637285378280023, 23.515604315240033, 23.594912277922973, 23.649875136387475, 23.505921609914147, 23.30230219819553, 23.255901647048383]
However, the way in which we've calculated this information, by manually sliding a current window through the length of the price list, is different from our original Clojure version. The Java version forces us to not just declare types for each program element. It's also a more manual process of managing how lists are processed and all the intermediate types and steps required in this traversal. Clojure's partition
function lazily gave us a subset of an entire list. reduce
walks over this list of lists to calculate the average value of each one. Nowhere did we manually manage the order of operations to walk a list. We simply gave calculation instructions at each step. This is what's meant by a more declarative style of programming (you can read more at https://en.wikipedia.org/wiki/Declarative_programming).
18.116.15.161