Addem
Addem

Reputation: 3919

Difference between static types and dynamic classes in Java

I've seen several resources talk about the difference between static types and dynamic types, and the difference between types and classes. However, it's either a different matter to talk abut static types and dynamic classes, or I'm just not getting the concepts enough to figure it out given what I already know.

To give a concrete case, take the code

public interface I {}

public class C {}

public class E extends C implements I {}

public class F extends E {}

Object a = new E();

C b = new E();

C c = new F();

As I currently understand it, the static type is always given by the type annotation, so that a has type Object and the others type C. The dynamic class I understand less well, but my best guess is that it's always based on the new type. So a and b have dynamic class E while c has F. I'd appreciate any insights or corrections.

Upvotes: 2

Views: 6584

Answers (1)

madhead
madhead

Reputation: 33422

Difference between static types and dynamic types

Roughly saying, statically typed languages are those in which types are known at compile time. Dynamically typed languages are those in which types are checked at runtime. There is a third category here, untyped languages, like or . But we're not speaking about them. Java and are examples of statically typed languages for JVM. is a dynamically typed. Here, read this article about types in programming languages.

So, there is no "dynamic types" when speaking about Java. All types are already known before the program executes. What you may be tricked with is called polymorphism. It simply means that a "Car" can be either "Truck" or "Minivan", an animal can be a "Cat", a "Dog", or even a "Snake". There can be intermediate types, like "Mammals" or "Lizards". But it means that all this things have something in common. I.e. "Cars" have wheels (at least as of end of 2017 there is not much flying cars) and drive on the roads. "Animals"... Well, "Animals" may be a bad, very generic example, but they all have carbon in their cells. "Mammals" have hot blood and "Lizards" - cold. You got the idea. The more specific the type is, the more properties it specifies. Subtypes can override and specify properties and methods from supertypes. At the very end of the hierarchy there are concrete instantiatable classes, like "DeLorean DMC-12" or "Abyssinian cat".

Let's think about cars. There may be a hierarchy like this:

public interface Car {
    String manufacturer();
}

public interface PassengerCar {
    Integer numberOfSeats();
}

public interface TimeMachine {
    void travelTo(Date destination);
}

public class DMC12 implements PassengerCar {
    @Override
    public String manufacturer() { return "DeLorean Motor Company"; }

    @Override
    public Integer numberOfSeats() { return 2; }
}

pubblic class DeLoreanTimeMachine extends DMC12 implements TimeMachine {
    private FluxCapacitor fluxCapacitor = new FluxCapacitor();

    @Override
    public void travelTo(Date destination) { fluxCapacitor.travelTo(destination); }
}

Even below classes there are objects. Objects are concrete instances of classes. Back to our examples, there may be "Dr. Emmett Brown's time machine", instance of DeLoreanTimeMachine:

final DeLoreanTimeMachine docsCar = new DeLoreanTimeMachine();

But, as we know DeLoreanTimeMachine is still a DMC12 car, a TimeMachine, a PassengerCar, and, finally, a Car, so these expressions are valid as well:

final DMC12 dmc12 = new DeLoreanTimeMachine();
final TimeMachine timeMachine = new DeLoreanTimeMachine();
final PassengerCar passengerCar = new DeLoreanTimeMachine();
final Car car = new DeLoreanTimeMachine();

The difference is the set of methods that you will be able to access through such a reference. In a statically typed language, the type of car known to the compile will be just a Car, so all you will be able to do is car.manufacturer(). Even if it is a full-featured time machine with a powerful flux capacitor that can help you travel to the 1985 you cannot use (without extra tricks) that functionality. What a waste! Similarly, you can only know number of seats through passengerCar reference, only travel through time with timeMachine and so on.

There may be other classes (and thus objects) implementing and extending these interfaces. In different combinations. TARDIS is a time machine, but not a Car. Optimus Prime is a Car, a Truck and a Transformer (those classes are not in our hierarchy), but he cannot travel through time (can he?).

But what is that trick you told about? Well, a bunch of them, united by a term . What you can do with reflection?

You can know real run time types and do casts! E.g.

passengerCar instanceOf Car
passengerCar instanceOf TimeMachine
passengerCar instanceOf DMC12
passengerCar instanceOf DeLoreanTimeMachine

will all be true! Because actually a passengerCar in our examples was a Doc's car. Even the type known to the compile is PassengerCar and it does not allow you to call methods from other classes / interfaces, you'll be able to do this hack:

((DeLoreanTimeMachine) passengerCar).travelTo("Oct. 26, 1985 1:21 A.M");

Whoa! You've just tear apart the continuum and travelled to the 1985! Quick, get into the car and watch out for the Lybian terrorists!

But these will fail with ClassCastException:

((PassengerCar) tardis).numberOfSeats(); // you'd expect it to be 1 or what?
((TimeMachine) optimusPrime).travelTo("Oct. 26, 1985 1:21 A.M"); // wrong universe, pal

And here we met another typing "dimention". Strong vs weak. Just remember that Java is strongly (and statically) typed and does not allow you to do that (for your own good). But in Groovy you'll be able to override methodMissing and respond to literally any call from any class. That is, kind of weak typing.

One more thing about types in Java. You should know about type erasure. It is applied to generic types and simply means that on a runtime the types of objects may be stripped down to Object (or well-known upper bound), thus, making that variables kind of untyped. But the compiler generates some extra type checks and casts in case of generics so you'll be fine.

Difference between types and classes

They are almost the same, but in Java you also have primitives, which are types, but not classes. They can be auto-wrapped into corresponding classes, though. Some languages, like treat everything as an object, thus having a class type (no primitives). But don't pay much attention to these concepts and remember that they can be used interchangeably.

As I currently understand it, the static type is always given by the type annotation

The dynamic class I understand less well, but my best guess is that it's always based on the new type

Well, yes, I understand what you mean. Just replace "static type" with something like "compile time type" and "dynamic class" with "runtime type" and you'll be 100% correct.

To recap: a compile time type is a type known to a compiler and it restricts the set of methods you can call in your source code. A runtime type may be different, but it always should be a subtype of a compile time type. You can do casts and access methods from a runtime type, but it may not be safe. Type erasure erasures generic types from a bytecode but compiler is smart enough to insert safe casts and type checks.

Upvotes: 8

Related Questions