You are on page 1of 15

See discussions, stats, and author profiles for this publication at: https://www.researchgate.

net/publication/351088042

Java Programming Language Report

Technical Report · January 2021


DOI: 10.13140/RG.2.2.33652.48005

CITATIONS READS

0 420

1 author:

Mahmud Jabri
Utrecht University
3 PUBLICATIONS   0 CITATIONS   

SEE PROFILE

All content following this page was uploaded by Mahmud Jabri on 24 April 2021.

The user has requested enhancement of the downloaded file.


Java Programming Language
Report

Mahmud Jabri
6356672

Computing Science
Utrecht University
January 2021
Contents
1 Introduction 2

2 Language Features 2
2.1 Programming Paradigm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.1.1 Object Oriented . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1.2 Generic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1.3 Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2 Typing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2.1 Union Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2.2 Intersection Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2.3 Scoping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3 Type Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.4 Type Safety . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.5 Functional Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5.1 Higher Order Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5.2 Partial Application of a Method . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.5.3 Closure In Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3 Java Pros and Cons 11

1
1 Introduction
Java is a general purpose programming language that was developed by Sun Microsystems and
released in 1995. It is influenced by C++. The main motivation behind Java was to make a
programming language that allows its users to “write once, run everywhere”. The difference between
the way Java worked, and other programming languages at that time was revolutionary. Instead
of being compiled into a specific machine code, the Java compiler turns code into something called
Bytecode, which is then interpreted by a software called the Java Runtime Environment JRE, or
the Java Virtual Machine JVM. The JRE acts as a virtual computer that interprets Bytecode and
translates it for the host computer. Because of this, Java code can be written the same way for many
platforms (“write once, run anywhere”), which helped lead to its popularity for use on the Internet,
where many different types of computers may retrieve the same web page.[3] Java is the third most
used language for the last 5 years on GitHub [8]. Different platforms of Java are available depending
on the type of application for which Java will be used to develop. There are four platforms of the
Java programming language:[11]

• Java standard edition SE is the standard API of the language along with the JVM and Java
compiler.
• Java enterprise edition EE is built on the top of Java SE with extra components to support the
enterprise projects that require large-scale, multi-tiered, scalable, reliable, and secure network
applications.

• Java micro edition ME provides an API and a small-footprint JVM for running Java applica-
tions on small devices, like mobile phones. The API is a subset of the Java SE API. Java ME
applications are often clients of Java EE platform services.
• JavaFX is a platform for creating rich internet applications using a lightweight user-interface
API.

In this report we will not go into difference between platforms. Instead, we will dive into the
language features that are not limited to a specific platform. Since it is almost impossible to discuss
the plenty of features Java comprehensively in such limited number of pages, this report will only
discuss some of Java features.
The syntax of Java is largely influenced by C++ and C. Unlike C++, which combines the syntax
for structured, generic, and object-oriented programming, Java was built almost exclusively as an
object-oriented language. All code is written inside classes, and every data item is an object, with
the exception of the primitive data types, (i.e. integers, floating-point numbers, boolean values, and
characters), which are not objects for performance reasons. Unlike C++, Java does not support
operator overloading or multiple inheritance for classes, though multiple inheritance is supported
for interfaces.

2 Language Features
2.1 Programming Paradigm
Java as other modern general purpose programming languages started with fewer programming
paradigms and kept adapting to new paradigms as the language editions kept evolving. With its
recent editions we can consider Java as a language that supports multi-paradigms.

2
2.1.1 Object Oriented
Java is an Object Oriented Programming OOP language at heart. OOP languages model the world
by classes. Every class has its own attributes, also known as fields, and a number of methods that
might change the state of those fields, also known as behavior. An object is a real instance of a certain
class. Similarly to the real world, classes can be part of other classes that are usually less restricted.
Therefore, they can inherit their certain fields and methods from their parent class i.e., superclass.
Actually every class defined in Java’s API or any class the programmer defines themselves inherits
directly or indirectly from the foundation class Object, that is the common superclass of every other
class written in Java.
Both class and interface in Java define a new type. Therefore, we need to distinguish between
two kinds of types in Java. First, a reference type can be defined using a class or an interface and this
represents the type extensibility feature in Java. Second, we have primitive types that are basically
the types of commonly used variables such as: int, long, short, float, double, boolean and char.
These types are kept non-reference types only for performance reasons.

2.1.2 Generic
Generic programming, as known as parameterized types in functional languages, is a programming
style that aims to write general algorithms that are not defined for specific types, rather they are
instantiated later when specific types parameters are provided. This style allows a certain algorithm
or a function to be written once and operate on different types determined at run-time. Java allows
generic programming at class and method level. Below is a code snippet for a parameterized type,
i.e, class.
1 class Test <T >{
2 // An object of type T is declared
3 T obj ;
4 Test ( T obj ) { this . obj = obj ; } // constructor
5 public T getObject () { return this . obj ; }}

In addition to parameterized types, Java supports parameterized methods as shown in the following
code snippet.
1 // A Generic method example
2 static <T > void g enericDi splay ( T element ) {
3 System . out . println ( element . getClass () . getName () +
4 " = " + element ) ; }

2.1.3 Polymorphism
Apart from generics which is considered as parametric polymorphism in functional languages, two
other types of polymorphism exist in OOP languages including Java[13]. The types are:
• Compile-time polymorphism. It is also known as static polymorphism, or adhoc polymor-
phism. This type of polymorphism is achieved by method overloading or operator overloading.
However, Java does not support operator overloading.
• Run-time polymorphism. It is also known as dynamic method dispatch. It is a process
in which a function’s call to the overridden method is resolved at Runtime. This type of
polymorphism is achieved by method overriding.

3
2.2 Typing
Programming languages can be divided, from typing perspective, into: statically typed, dynamically
typed and untyped languages. Java is a statically typed language which means all or almost all
checking of types is done as a part of compilation i.e., in the compilation time. This is in contrast to
dynamically typed languages where most of type checking happens during the program execution.
The Java programming language is also a strongly typed language, because types limit the values
that a variable can hold or that an expression can produce, limit the operations supported on those
values, and determine the meaning of the operations. Strong static typing helps detect errors at
compile-time.[5]

2.2.1 Union Types


Since the world around us is not always deterministic, we need a way to model this functionality in
programming languages. Java allows the concept of union types by class inheritance. When a class
A inherits a class B this reflects, according to Java, that B is A relationship holds. This, allows a
method that expects an argument from type A to accept a parameter of type B, as well as, type A.
Finally, although Java limits inheritance to one super class which might look at the beginning as
a restriction on union types to 2 types only, it does allow multiple interface implementation. The
following code example shows how class inheritance resembles two types union type argument for
method printLine.
1 public class test {
2
3 public static void main ( String [] args ) {
4 ClassA ref1 = new ClassA () ;
5 ClassB ref2 = new ClassB () ;
6
7 printLine ( ref1 ) ;
8 printLine ( ref2 ) ;
9 }
10
11 static void printLine ( ClassA arg )
12 {
13 System . out . println ( arg ) ;
14 }
15 }
16
17 class ClassA {}
18 class ClassB extends ClassA {}

It is important to note that it is also possible to implement union type through a class that imple-
ments an interface, then a method accepts a parameter of that class type can accept the interface
type as well. In addition to that, interface inheritance of other interfaces can emulate union types
also.

2.2.2 Intersection Types


Allowing intersection types is a very powerful feature that allows the types in a language to expand
enormously. Many times we do need a type that represents the joint effect of two types or more.
Java provides different ways to implement intersection types. Many scenarios can be seen as types
intersections, but we will see examples that show how to use an intersection of two types to satisfy the
argument of a method. First, imagine two types defined by interfaces Flyable and Swimable [12]. One
way to have a type that represents both Flyable and Swimable, is to define a class SailfinFlyingfish

4
that implements both interfaces Flayable and Swimable. However, a problem might arise when we
want to define a method that accepts an argument of type both Flayable and Swimable.
1 public static void process (??? animal ) {...}

A first solution can be achieved by defining an interface interface FlyableAndSwimmable that extends
both Flyable and Swimmable, then making the new interface as the type of the method’s parameter.
1 public static void process ( F l y a b l e A n d S w i m m a b l e animal ) {...}

Although this solution is type safe at compile time, it does require writing unnecessary extra piece
of software, that is, the definition of FlyableAndSwimmable interface. A second solution can be as
it is shown in the following code snippet:
1 public static void process ( Flyable animal ) {
2 if ( animal instaceof Swimable )
3 {
4 animal . fly () ;
5 (( Swimable ) animal ) . swim () ;
6
7 }
8 else {
9 throw new I l l e g a l A r g u m e n t E x c e p t i o n () ;
10 }
11 }

The disadvantage of this solution is that the method process() would accept a flying but not swim-
ming animal, and explore at run-time instead of compile-time. This solution is not even type safe.
A third solution is proposed using Java generics capabilities, as we have seen in section 2.1.2 Java al-
lows methods bounded parameterized arguments to be used. This would allow us to define process()
like the following:
1 method public static <T extends Flyable & Swimmable > void process ( T animal ) {...}

By defining process() method that way, we are using explicit intersection of two existing types not
boxed explicitly in a wrapper type, by doing so we do not restrict the clients of the method to a
particular wrappers. In addition to that, this implementation is type safe at compile-time.

2.2.3 Scoping
Java uses lexical scoping rules for its elements declarations. One of the things lexical scoping means
is that the scope resolution for an identifier is determined by compile-time. Here are some scoping
of some Java’s declaration as mentioned in [6].
The scope of a declaration is the region of the program within which the entity declared by the
declaration can be referred to using a simple name, provided it is not shadowed. A declaration is
said to be in scope at a particular point in a program if and only if the declaration’s scope includes
that point. Here are some example of some Java language items scope:
• The scope of a top level type e.g., classes and interfaces, is all type declarations in the package
in which the top level type is declared.
• The scope of a declaration of a member m declared in or inherited by a class, or interface type
C is the entire body of C, including any nested type declarations.
• The scope of a local variable declaration in a block is the rest of the block in which the
declaration appears, starting with its own initializer and including any further declarators to
the right in the local variable declaration statement.

5
The following code shows how the scope of class type PointList is entire file on which it is declared.
1 package points ;
2 class Point {
3 int x , y ;
4 PointList list ;
5 Point next ;
6 }
7
8 class PointList {
9 Point first ;
10 }

Shadowing happens when the scopes of two variables of the same name interfere. Normally one
of the variables is declared globally, and the other is declared locally. However, it is possible also
between local variables. Here are two code examples that shows when it is possible to shadow and
when it is a compile-time error.
1 class Test1 {
2 public static void main ( String [] args ) {
3 int i ;
4 for ( int i = 0; i < 10; i ++)
5 System . out . println ( i ) ;}
6 }

The code above gives a compilation error because a local variable can not shadow another local
variable.
The following code will compile without errors and returns numbers from 0 to 9 of the local
variable i and 100 for the local i that is declared in the main method.
1 public class Test2 {
2 public static void main ( String [] args ) {
3 int i =100;
4 class Local
5 {
6 { for ( int i = 0; i < 10; i ++)
7 System . out . println ( i ) ;
8 }
9 Local ()
10 {
11 System . out . println ( " i = " + i ) ;
12 }
13 }
14 new Local () ;
15 }
16 }

Obscuring happens when it is impossible to refer to a type or package via its simple name, even
though its declaration is in scope and not shadowed. Java has rules to prefer what over what [7].

2.3 Type Inference


The introduction of generic programming into Java has made another requirement that Java com-
piler needs to meet. Type inference is the compiler’s capability to infer correct types to untyped
expressions. Our discussion and examples in this section are based on [2]. First, we start with type
inference for generic methods. Consider the following generic method declaration:
1 public static <U > void addBox ( U u , java . util . List < Box <U > > boxes )

6
To invoke the generic method addBox, you can specify the type parameter with a type witness as
follows
1 BoxDemo . < Integer > addBox ( Integer . valueOf (10) , l i s t O f I n t e g e r B o x e s ) ;

Alternatively, if you omit the type witness, a Java compiler automatically infers (from the method’s
arguments) that the type parameter is Integer:
1 BoxDemo . addBox ( Integer . valueOf (20) , l i s t O f I n t e g e r B o x e s ) ;

Second, we see an example of type inference and instantiation of generic classes. Consider the
following code:
1 Map < String , List < String > > myMap = new HashMap < String , List < String > >() ;

You can replace the type arguments required to invoke the constructor of a generic class with an
empty set of type parameters (<>) as long as the compiler can infer the type arguments from the
context. Hence, you can instantiate that object as follows:
1 Map < String , List < String > > myMap = new HashMap < >() ;

Third, in addition to generic methods, and class instantiation Java compiler can infer types of a
generic constructor for a generic and non-generic class. The following snippet demonstrates this
inference:
1 class MyClass <X > {
2 <T > MyClass ( T t ) {
3 // ...
4 }
5 }

Here we have a parameterized class and parameterized constructor. We can instantiate them both
in a statement such as:
1 new MyClass < Integer >( " " ) ;

MyClass is instantiated over Integer type and its constructor is instantiated over String type.
Finally, we introduce Java compiler type inference capability using target types. The target type
of an expression is the data type that the Java compiler expects depending on where the expression
appears. Consider the following method’s declaration:
1 static <T > List <T > emptyList () ;

This method takes no arguments and returns a List of parameterized type T. Now consider the
following statement:
1 List < String > listOne = Collections . emptyList () ;

This statement is expecting an instance of List<String>; this data type is the target type. Because
the method emptyList returns a value of type List<T>, the compiler infers that the type argument
T must be the value String. The notion of target type is also expanded in Java 8 and beyond to
include method arguments.
In conclusion, the inference algorithm determines the types of the arguments and, if available,
the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find
the most specific type that works with all of the arguments. Note: It is important to note that the
inference algorithm uses only invocation arguments, target types, and possibly an obvious expected
return type to infer types. The inference algorithm does not use results from later in the program.

7
2.4 Type Safety
Being type safe i.e., strongly typed, language guarantees that no error in the syntax can happen.
However, this is only part of what do we mean by error in programming languages in general. Let
us take as an example a partial function that divides two integers and return the result rounded to
the nearest integer. This method can be well typed but we might give the denominator the value 0
and then while executing the program gets into a stuck state. These stuck states that can happen
in the run-time are something a safe language needs to address. Java provides several solutions to
ensure run-time safety depending on the nature of the stuck state. There are two kind of stuck states
depending on their causes. The first kind of stuck states that can originate from uncaptured flaws
between the static and dynamic semantics. As an example of such stuck states, when the language
user who is trying to make undefined division for instance, or trying to access non existing file for
instance. Java calls such states as Exceptions. The second type of such states, would be the stuck
states the JVM might encounter due to an internal error. For example, stack over flow error, or
having accessed a corrupted memory location etc. Java considers such states as Errors.
As many other programming languages exception causing code requires to be written in a special
block of a try statement. Being in a try statement every expression will be evaluated in different
dynamic semantics than a normal code. The following example demonstrates Java exception han-
dling.
1 // Java program to demonstrate how exception is thrown .
2 class ThrowsExecp {
3
4 public static void main ( String args []) {
5
6 String str = null ;
7 System . out . println ( str . length () ) ;
8 System . out . println ( " Will never be reached " ) ;
9
10 }
11 }

The output for this code will be : Exception in thread ”main” java.lang.NullPointerException at
ThrowsExecp.main(File.java:8). What happened here, is that after trying to use a null reference,
and that led our program to a stuck state that terminated the program without reaching the next
statement.
Now by using Java type safety mechanism, mainly try-catch. The same code will run normally.
1 public class ThrowsExecp {
2 public static void main ( String args []) {
3 String str = null ;
4 try { System . out . println ( str . length () ) ;}
5 catch ( N u l l P o i n t e r E x c e p t i o n e ) {\\ do something }
6 System . out . println ( " Will be reached " ) ;
7 }}

The output for this code will be: Will be reached. This is because try introduces different
dynamic semantics and returns/throw an exception that gets processed in the catch block. Multiple
catch blocks can be used to catch different type of exceptions that might occur in the try block. In
addition to that, a user can define his own exceptions and throw them to ensure certain business-
special stuck cases to be safe as well. This can occur through the use of throw keyword that can
change the control flow. Another keyword worth knowing is finally that can be optionally added to
the end of catch block(s) to indicate a block that contains all the crucial statements that must be
executed whether exception occurs or not. Moreover, Java does not only offer try-catch processing

8
as an optional choice to the programmer, instead, in some cases it does force the programmer on
declare or handle mechanism. That is for some statements that are likely to cause an exception
such as IO operations. The declare or handle mechanism forces the code block, at which such
statements appear, to either handle the possible exception by a proper try-catch, or to indicate to
the calling method that it is calling a possible exception-causing method, this indication happens
by the keyword throws

2.5 Functional Programming


Although Java is OOP language at heart as stated earlier, in Java 8, the language added the ability to
write code using another style. Functional programming is a way of writing code more declaratively.
You specify what you want to do rather than dealing with the state of objects. You focus more on
expressions than loops. In this section we will discuss some of the functional capabilities that are
adopted in Java recently.

2.5.1 Higher Order Functions


The ability to pass code around as any variable is one of the essential features of a functional pro-
gramming language. Java supports this feature through lambda expressions. A lambda expression
is a block of code that gets passed around. In this section we will explore a motivation example
on lambda expressions in Java from[14]. We want to perform some tests on a collection of different
animals. We first define the class Animal with its fields and methods.
1 public class Animal {
2 private String species ;
3 private boolean canHop ;
4 private boolean canSwim ;
5 public Animal ( String speciesName , boolean hopper , boolean swimmer ) {
6 species = speciesName ;
7 canHop = hopper ;
8 canSwim = swimmer ;
9 }
10 public boolean canHop () { return canHop ; }
11 public boolean canSwim () { return canSwim ; }
12 public String toString () { return species ; }
13 }

We will define an interface that would declare a test method that’s supposed to test for a given
animal a certain trait.
1 public interface CheckTrait {
2 boolean test ( Animal a ) ;
3 }

Of course, we need a class to implement the method test from the interface CheckTrait
1 public class CheckIfHopper implements CheckTrait {
2 public boolean test ( Animal a ) {
3 return a . canHop () ;
4 }
5 }

Finally we implement a program to instantiate a list of several animals and check the hopping trait
for each one in the list by passing it to the method print.
1
2 public class T r a d i t i o n a l S e a r c h {

9
3 public static void main ( String [] args ) {
4 List < Animal > animals = new ArrayList < Animal >() ; // list of animals
5 animals . add ( new Animal ( " fish " , false , true ) ) ;
6 animals . add ( new Animal ( " kangaroo " , true , false ) ) ;
7 animals . add ( new Animal ( " rabbit " , true , false ) ) ;
8 animals . add ( new Animal ( " turtle " , false , true ) ) ;
9
10 print ( animals , new CheckIfHopper () ) ; // pass class that does check
11 }
12 private static void print ( List < Animal > animals , CheckTrait checker ) {
13 for ( Animal animal : animals ) {
14 if ( checker . test ( animal ) ) // the general check
15 System . out . print ( animal + " " ) ;
16 }
17 System . out . println () ;
18 }
19 }

While this design might look complicated, it is good software engineering design that promotes
separation of software entities. However, it is costly when we need to extend this and check another
trait say swimming. This requires us to provide another class, for example class CheckIfSwimmer
that implements the method test from the interface and adds a similar call to the method print in
line 10 with the reference animal and CheckIfSwimmer object. But what if we can just pass the part
that does the real job? Which is in our case, canHop() function itself or canSwim() to the method
print as an argument. Turns out that we can with lambda expressions. By using the code below
instead of line 10 in the previous code, we can omit the entire definition of class CheckIfHopper.
1 print ( animals , a -> a . canHop () ) ;

Java does the type checking for the line of code above this time from the context. Since the
print method is defined to have a type interface Checker as the second argument, and since that
interface declares one method only that takes arguments of type Animal, the Java compiler can
relate the variable (a) in our lambda expression to the Animal type. Since class Animal defines a
function canHop() that returns boolean, then Java compiler also can figure out the return type of
(a.canHop()). From such a simple trick Java 8 and beyond allowed the users to use very powerful
functional programming syntax in OOP language. Although the support of functional programming
in modern releases of Java is far more than what we have discussed so far, we will tie ourselves to
this part.

2.5.2 Partial Application of a Method


As part of Java adopting to functional paradigm from Java SE 8, the language designers provided
functional interface. That is an interface that contains only a single abstract (unimplemented)
method[9]. A functional interface can contain default and static methods which do have an im-
plementation, in addition to the single unimplemented method. Using Functional Interface partial
function application became possible. We will demonstrate this with a simple example motivated
from [4]
1 import java . util . function . Function ;
2 import java . util . function . BiFunction ;
3 public class Example
4 { public static int multiply ( int x , int y ) { return x * y ;}
5 public static <A , B , R > Function <B , R > partial ( BiFunction <A , B , R > f , A x ) {
6 return ( y ) -> f . apply (x , y ) ;}
7 public static void main ( String [] args ) {
8 Function < Integer , Integer > mul5 = partial ( Example :: multiply , 5) ;

10
9 System . out . println ( mul5 . apply (2) ) ;}}

Simply we have a two arguments function multiply that takes x and y and return their product.
We define partial function that takes a two-arguments function f and one parameter x and applies
partially the function f to the parameter x then returns a result of type Function < B, R > where B
is the second parameter and R is the result. In line 8 partial method has been given multiply function
and a value 5 then, the result assigned to mul5 is applied to the value 2 as a second parameter. Also,
this example shows that Java does support some sort of higher order functions as happens when
partial method has been given multiply method as a parameter.

2.5.3 Closure In Java


Objects in OOP paradigm contain both data i.e., state, and methods that could work on the data.
However, in functional programming we thrive to make functions pure i.e., functions are preferred
not to change the state of the world. Hence, a closure that is a function that has an access to
its surrounding environment is useful in functional context. Java, albeit object oriented language,
provides a sort of closure support as part of its support to functional programming.
Here are some code examples that demonstrated closure in Java, these examples are motivated
from [10].
1 import java . util . function . Function ;
2 public class test {
3
4 static Function < Integer , String > g e t T e x t O f W e e k D a y ()
5 {
6 String [] weeks = { " Mon " ," Tue " ," Wed " ," Thr " ," Fri " ," Sat " ," Sun " };
7
8 return num -> ( num > 0 && num <= weeks . length ) ? weeks [ num -1] : null ;
9 }
10
11 static void cl osureExa mple ()
12 {
13
14 System . out . println ( g e t T e x t O f W e e k D ay () . apply (2) ) ;
15 }
16
17 public static void main ( String args [])
18 {
19 closu reExamp le () ;
20 }
21 }

3 Java Pros and Cons


So far we have discussed mostly the features of Java programming language. In this section we will
go through a brief summary of Java strengths again and in more details about some of its criticism.
Java strengths rely on it is OOP language that is supported by a huge class library API that has
almost everything the developer needs. In addition to that, Java is secure since it runs on ”sandbox”
model to prevent potential malicious code to have access to certain platform features and APIs which
could be exploited by malware, such as accessing the local filesystem, running arbitrary commands,
or accessing communication networks. Java has been there for around 25 years and that makes it
adopted by a big community of developers and makes the internet very rich in Java’s resources and
tutorials.

11
On the other hand, Java has many criticism that are worth looking at[1]. First, when generics
were added to Java 5.0, there was already a large framework of classes (many of which were already
deprecated), so generics were chosen to be implemented using type erasure to allow for migration
compatibility and re-use of these existing classes. This limited the features that could be provided
by this addition as compared to other languages. Because generics were implemented using type
erasure the actual type of a common template parameter E is unavailable at run-time. Thus, the
following operations are not possible in Java:
1 public class MyClass <E > {
2 public static void myMethod ( Object item ) {
3 if ( item instanceof E ) { // Compiler error
4 ...
5 }
6 E item2 = new E () ; // Compiler error
7 E [] iArray = new E [10]; // Compiler error
8 }
9 }

Another point of criticism is that Java does not support operators overloading as we mentioned in
2.1.3. Moreover, the early versions of JVMs had remarkable performance issues, however, recent
JVMs have significantly improved performance.
In sections 2.2.1 and 2.2.2 we saw that Java does support both the Sum, or union, and the
Product types that are the core of the powerful algebraic data types ADTs concept. However, Java
lacks the most powerful feature, that makes ADTs easier to manipulate, that is pattern matching.
Moreover, recursive parameterized data types are possible to some extent to be implemented in Java,
however their syntax is way more complicated than Haskell programming language for instance. The
following code snippets show the difference in writing some recursive tree structure in Java vs Haskell.
1 abstract class Tree <T > {}
2 class Leaf <T > extends Tree <T > {
3 public T value ;
4
5 public Leaf ( T value ) {
6 this . value = value ;
7 }
8 }
9
10 class Branch <T > extends Tree <T > {
11 public Tree <T > left , right ;
12
13 public Branch ( Tree <T > left , Tree <T > right ) {
14 this . left = left ;
15 this . right = right ;
16 }
17 }

Here is the corresponding Haskell code:


1 data Tree t = Leaf t | Branch ( Tree t ) ( Tree t )

In conclusion, Java is a very powerful multi-paradigm general purpose programming language,


that can be suitable for lots of applications and has been used in many enterprise applications
for over 20 years. On the other hand, Java designers are facing a challenging task of adapting to
declarative programming capabilities while maintaining Java’s OOP nature, and the right balance
between these two is going to be the key of Java’s future.

12
References
[1] Java criticisms. https://en.wikipedia.org/wiki/Criticismo fJ ava, 2020.
[2] Type inference. https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html,
2020.
[3] Britannica. Java programming language. ”https://www.britannica.com/technology/Java-
computer-programming-language”.
[4] DENIS BYKOV. Partial function application and currying in java.
https://engineering.sellerlabs.com/2020/04/partial-function-application-and-currying-in-java/,
2020.

[5] Gosling et al. ”java language specifications”. https://docs.oracle.com/javase/specs/jls/se7/html/index.html,


”2013”.
[6] Gosling et al. Java language specifications ch6. names and scoping.
https://docs.oracle.com/javase/specs/jls/se15/html/jls-6.html, 2020.
[7] Gosling et al. Reclassification of contextually ambiguous names.
https://docs.oracle.com/javase/specs/jls/se15/html/jls-6.htmljls-6.5.2, 2020.
[8] Github. ”https://madnight.github.io/githut/#/pull requests/2015/4”.
[9] Jakob Jenkov. Java functional interfaces. http://tutorials.jenkov.com/java-functional-
programming/functional-interfaces.html , 2019.

[10] Ramkumar Manavalan. 5 ways to implement closures in java 8.


https://medium.com/@rmanavalan/5-ways-to-implement-closures-in-java-8-590790659ac5,
2018.
[11] Oracle. Your first cup: An introduction to the java ee platform.
https://docs.oracle.com/javaee/6/firstcup/doc/gkhoy.html.

[12] Grzegorz Piwowarek. Leveraging intersection types in java.


https://4comprehension.com/leveraging-intersection-types-in-java/, 2019.
[13] PREMMAURYA. Polymorphism in java. https://www.geeksforgeeks.org/polymorphism-in-
java/, 2020.

[14] Jeanne Boyarsky Scott Selikoff. Oracle Certified Associate Java SE 8 Programer I. John Wiley
Sons Inc, 2015.

13

View publication stats

You might also like