Java – Interesting Bits – Static and Dynamic Checking…

Static/Dynamic vs Strong/Weak

Simply put it this way: in a statically typed language the type is static, meaning once you set a variable to a type, you CANNOT change it. That is because typing is associated with the variable rather than the value it refers to.

Java is a statically-typed language. The types of all variables are known at compile time (before the program runs), and the compiler can therefore deduce the types of all expressions as well. If a and b are declared as ints, then the compiler concludes that a+b is also an int. The Eclipse environment does this while you’re writing the code, in fact, so you find out about many errors while you’re still typing.

For example in Java:

String str = "Hello";  //statically typed as string
str = 5;               //would throw an error since java is statically typed

Whereas in a dynamically typed language the type is dynamic, meaning after you set a variable to a type, you CAN change it. That is because typing is associated with the value rather than the variable.  In dynamically-typed languages like Python, this kind of checking is deferred until runtime (while the program is running).

For example in Python:

str = "Hello" # it is a string
str = 5       # now it is an integer; perfectly OK

On the other hand, the strong/weak typing in a language is related to implicit type conversions (partly taken from @Dario’s answer):

For example in Python:

str = 5 + "hello" 
# would throw an error since it does not want to cast one type to the other implicitly. 

whereas in PHP:

$str = 5 + "hello"; // equals 5 because "hello" is implicitly casted to 0 
// PHP is weakly typed, thus is a very forgiving language.

Static typing allows for checking type correctness at compile time. Statically typed languages are usually compiled, and dynamically typed languages are interpreted. Therefore, dynamically typed languages can check typing at run time. (http://stackoverflow.com/questions/2351190/static-dynamic-vs-strong-weak)

More info: http://blogs.perl.org/users/ovid/2010/08/what-to-know-before-debating-type-systems.html

Static Checking, Dynamic Checking, No Checking

It’s useful to think about three kinds of automatic checking that a language can provide:

  • Static checking: the bug is found automatically before the program even runs.
  • Dynamic checking: the bug is found automatically when the code is executed.
  • No checking: the language doesn’t help you find the error at all. You have to watch for it yourself, or end up with wrong answers.

Needless to say, catching a bug statically is better than catching it dynamically, and catching it dynamically is better than not catching it at all.

Here are some rules of thumb for what errors you can expect to be caught at each of these times.

Static checking can catch:

  • syntax errors, like extra punctuation or spurious words. Even dynamically-typed languages like Python do this kind of static checking. If you have an indentation error in your Python program, you’ll find out before the program starts running.
  • wrong names, like Math.sine(2). (The right name is sin.)
  • wrong number of arguments, like Math.sin(30, 20).
  • wrong argument types, like Math.sin("30").
  • wrong return types, like return "30"; from a function that’s declared to return an int.

Dynamic checking can catch:

  • illegal argument values. For example, the integer expression x/y is only erroneous when y is actually zero; otherwise it works. So in this expression, divide-by-zero is not a static error, but a dynamic error.
  • unrepresentable return values, i.e., when the specific return value can’t be represented in the type.
  • out-of-range indexes, e.g., using a negative or too-large index on a string.
  • calling a method on a null object reference (null is like Python None).

Static checking tends to be about types, errors that are independent of the specific value that a variable has. A type is a set of values. Static typing guarantees that a variable will have some value from that set, but we don’t know until runtime exactly which value it has. So if the error would be caused only by certain values, like divide-by-zero or index-out-of-range then the compiler won’t raise a static error about it.  Tends to fail for every value.

Dynamic checking, by contrast, tends to be about errors caused by specific values.

Surprise: Primitive Types Are Not True Numbers

One trap in Java – and many other programming languages – is that its primitive numeric types have corner cases that do not behave like the integers and real numbers we’re used to. As a result, some errors that really should be dynamically checked are not checked at all. Here are the traps:

  • Integer division. 5/2 does not return a fraction, it returns a truncated integer. So this is an example of where what we might have hoped would be a dynamic error (because a fraction isn’t representable as an integer) frequently produces the wrong answer instead.  No checking – wrong answer.
  • Integer overflow. The int and long types are actually finite sets of integers, with maximum and minimum values. What happens when you do a computation whose answer is too positive or too negative to fit in that finite range? The computation quietly overflows (wraps around), and returns an integer from somewhere in the legal range but not the right answer.
  • Special values in floating-point types. Floating-point types like double have several special values that aren’t real numbers: NaN (which stands for “Not a Number”), POSITIVE_INFINITY, and NEGATIVE_INFINITY. So when you apply certain operations to a double that you’d expect to produce dynamic errors, like dividing by zero or taking the square root of a negative number, you will get one of these special values instead. If you keep computing with it, you’ll end up with a bad final answer.

Autoboxing and Unboxing

The Java compiler applies autoboxing when a primitive value is:

  • Passed as a parameter to a method that expects an object of the corresponding wrapper class.
  • Assigned to a variable of the corresponding wrapper class.The Java compiler applies unboxing when an object of a wrapper class is:
    • Passed as a parameter to a method that expects a value of the corresponding primitive type.
    • Assigned to a variable of the corresponding primitive type.

More info: https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

Logic Structures in Algorithms

There are 3 main logic structures which form the building blocks for algorithms, sequence, selection and iteration.

  • Sequence – the specific order in which instructions are performed.
  • Selection – decision which allows for branching, a different action dependent upon a question/condition.
  • Iteration – the process of repeating the steps / also referred to as a loop.

More info: http://www.bbc.co.uk/education/topics/z7d634j

Switch Statement

Each case of switch is supposed to be an integer or String since Java 7, a condition returns a boolean so that results in an incompatible type.   Consider the strings to be similar to labels, compared as if String.equals were being used.  Switch / case statements do not allow conditionals, but how about this beautiful and minimalist approach?  Worth looking into.

age > 79 ? first_case_method() 
          : age < 50 ? second_case_method() 
          : age < 40 ? third_case_method() 
          : age < 30 ? fourth_case_method() 
          : age < 20 ? fifth_case_method()
          : ... 
          : default_case_method();

Interfaces and Polymorphism

An interface is akin to an agreement/contract for a class, if a class implements an interface it is agreeing to implement all the methods specified in the interface definition.    Interfaces can contain abstract methods and fields, but typically contain no implementation code.  All methods in an interface need to be implemented in the class which implements the interface else the class will not compile. Interface fields are treated the same as static variables in the class.

Interfaces can extend multiple other interfaces, and inherits all fields and methods of the super interfaces, which could also be overridden in the base interface.   The class implementing the interface needs to implement all methods.

Since Java 8, provision has been made for default methods to alleviate the problem of developers adding to an API and existing classes no longer implementing all required methods.  A class can override the default implementation.  If a class implements multiple interfaces with matching default methods, complications arise.

Classes cannot extend multiple super classes owing to the Deadly Diamond of Death, however they can implement multiple interfaces.If a class implements multiple interfaces, there is a chance that method signatures may overlap (same name and parameters), which is not permitted in java.

Interfaces enable you to treat an object by the role it plays rather than by the class type from which it was instantiated. Once a class implements an interface you can use an instance of that class as an instance of that interface. Variables can be declared to be of the interface type.   If several objects implement an interface, then interface types can be used instead of class types in parameter declarations.  This provides flexibility.

Generally polymorphism is the provision of a single interface to different types.  It can be used at different levels, for example overloading methods so that they behave differently depending on the argument to which they are applied.  There can also be polymorphic data types, when a data type appears to be of a generalised type, specifically generics in Java.

Interfaces are a way to achieve polymorphism, it enables methods to be accessed by several classes, for example if you want one method to store various objects (from different class hierarchies) in a database, implement an interface called Storable.  When each class implements these methods, then they can be called to store the objects in a database without considering which particular type of object they are, all they need to be is Storable.

More info: http://tutorials.jenkov.com/java/interfaces.html

Equality

The equals method is defined in class Object, and since all objects in Java implicitly or explicitly inherit from this class, they too will inherit the equals() method as implemented by Object. The default implementation in Object will simply return true if the objects pass the “==” condition.

However, you are free to override the equals() method in your own class and specify the criteria which must be checked to see if two objects are meaningfully equal. For example, you might say that two instances are only equal if each of its attributes contain the same values as another object, or you might instead want to simply check a few attributes which make up the objects “business key” and ignore the others.

The equality of String classes follow the same rules as any other class in Java; “==” will be true if they do refer to the same instance, and equals() will be true if they contain the same values.

Nested Classes

Nested classes enable you to logically group classes that are only used in one place, increasing the use of encapsulation.  Nested classes include local classes (introduce a new named type), anonymous classes (don’t need to refer to it, e.g. inline ActionListeners for menus), lambda expression, inner class (similar to local class but more widely available and don’t need access to local context). Event handlers are a common use of inner classes. https://docs.oracle.com/javase/tutorial/java/javaOO/whentouse.html

InnerClass cannot have static members because it belongs to an instance (of OuterClass). If you declare InnerClass as static to detach it from the instance, your code will compile.

class OuterClass {
    static class InnerClass {
        static int i = 100; // no compile error
        static void f() { } // no compile error
    }
}

BTW: You’ll still be able to create instances of InnerClass.   static in this context allows that to happen without an enclosing instance of OuterClass.

You can use the same modifiers for inner classes that you use for other members of the outer class. For example, you can use the access specifiers private, public, and protected to restrict access to inner classes, just as you use them to restrict access do to other class members.  But you can’t use access modifiers when declaring a class within a method.

Primitives and Objects

Objects : defined in a class, instantiated, constructed and assigned to an identifier.  The have state, behaviour and identity.  They are stored on the heap.

Data types : Primitive types are special data types built into the language; they are not objects created from a class.  Primitives are stored on the stack.

Literal : A Literal is the source code representation of a fixed value; literals are represented directly in your code without requiring computation.  A literal can be a value of a primitive type [like 1, true, 't'or 1.2f], the String type [like "" or Something], or the null type [null].

boolean result = true;  // boolean - is data type
                        // true - is literal

Control Flow Structures

break terminates the loop (jumps to the code below it). continue terminates the rest of the processing of the code within the loop for the current iteration, but continues the loop.

int sum = 0;
for(int i = 1; i <= 100 ; i++){
    if(i % 2 == 0)
         continue;
    sum += i;
}

This would get the sum of only odd numbers from 1 to 100

More Info / Reading List

Leave a Reply

Your email address will not be published. Required fields are marked *