+ 02 April 2024, + + Joachim Schirrmacher + +
+ + + + + +My journey from TypeScript to Java - Part 1
+My journey from TypeScript to Java - Part 1
+For several years now, I’ve been programming in Typescript and enjoyed it significantly due to its simple and yet powerful type safety system in contrast to JavaScript.
+However, a lot of people in DB Systel use Java as their preferred programming language. +To understand what they like about Java, but also because it is easier to maintain the software in the team, I wanted to learn Java myself.
+My learnings on my way from TypeScript to Java might be useful for others going the same way. +While I’m going this way, I like to add more of these articles. +For now, I start with some beginner problems, but I already have prepared some more!
+-
+
-
+
Part 1
++ ++
+ -
+
Part 2 (planned)
+++-
+
-
+
Optional values and defaults
+
+ -
+
Objectifying mappings
+
+
+ -
+
-
+
part 3 (planned)
+++-
+
-
+
Enum-erable charm
+
+ -
+
Table manners for databases
+
+ -
+
Spice up your functions with curry
+
+
+ -
+
Many thanks to my friends who helped me with the formulation of my travelogue and showed me new ways:
+-
+
- + + +
-
+
Caroline Rieseler
+
+ -
+
Jasper Gerigk
+
+ - + + +
- + + +
No some objects are equal
+The first thing I stumbled upon might be a real beginner problem.
+I expected that I could compare strings very similar to TypeScript with the equality operator.
+Sure, the ===
operator in TypeScript is very special, but the IDE stops you from using it in Java.
However, it doesn’t stops you to use the 'normal' ==
equality operator for strings.
+Since String
is a class, and classes are only equal, if they are the same object, equality on two Strings normally doesn’t work.
+You might already know this behavior from TypeScript when, e.g. comparing `Date`s.
To make things more complicated, there are situations where comparisons work nevertheless:
+String param = "expected";
+if (param == "expected") {
+ System.out.println("These strings are equal!")
+}
+This happens because Java re-uses string literals like 'expected' in this example.
+So, the param
object and the "expected" string literal are actually equal!
However, if the String is created dynamically (e.g. from user input), it doesn’t:
+String param = " expected ".trim();
+if (param != "expected") {
+ System.out.println("These strings are not equal, even if they have the same content!")
+}
+In Java, comparing of objects is done with an equals()
method:
if (param.equals("expected")) {
+ System.out.println("Now the equality is recognized")
+}
+However, on primitive types, equality is implemented just like I would expect.
+So a == 5
works if a
is an int
.
+And, surprisingly, if you see a variable defined as Integer a
, you can use a == 5
, though Integer
is a class and a
therefore is an object of this class.
+Actually, the ==
operator defined here overloads the standard one, so the 'normal' rule of non-equality of objects if they are not the same is not applicable.
Try it out yourself:
+class Test {
+ public static void main(String[] args) {
+ Integer a = Integer.parseInt(args[0]);
+
+ if (a == 5) {
+ System.out.println("a is 5");
+ } else {
+ System.out.println("a is not 5");
+ }
+ }
+}
+In my opinion, this is not very intuitive, but that might be due to my past in TypeScript.
+Regrettably, Java implements function overloading only in a few cases (like the above) and doesn’t give us the opportunity to define them ourselves.
+Else, I would create a MyString
class and define a ==
method to compare strings like numbers.
Fun with null values
+Typescript inherits the sometimes difficult distinction between undefined
and null
from Javascript.
+Some people might think, this is not really necessary, but the two actually have a slightly different meaning: while null
means that I actually assigned a kind of empty value
to a variable or parameter, undefined
means 'not set'.
+null
is rather empty on purpose.
Java doesn’t make such a distinction, it only has null
- which might be sufficient for most use cases.
But how does both languages handle such values?
+Let’s say, you have a variable myObject
which might be an object having a function value()
but also may be undefined
.
+The function might return a Date or again undefined
.
+How can I compare this with the current time?
In Typescript this can be done with the so called 'Optional Chaining' like this:
+if (myObject?.value()?.getTime() > Date.now()) { + // ... +}+
The ?
means that when we get an undefined
, just stop evaluating, returning undefined
as the result of the expression.
+TypeScript programmers know this as 'falsy', which means that it something that is similar to the implementation of false
.
+Falsy in TypeScript are the values 0
, undefined
, null
, ""
(the empty string), and, of course, false
.
+This is a very compact and, if you understand the syntax, readable form.
In Java, there is no such thing as a optional chaining ?.
operator, we have to check every possible null value.
+Also, Java uses different types for numbers, int
or long
, so we need to decide, what value()
should return.
+If we want to use null
as well, both types won’t allow that.
But there is an alternative: instead of using the native int
or long
types, use the classes Integer
or Long
(with capital first letter) for the return value of value()
instead!
+They may be null (as all objects).
if (myObject != null
+ && myObject.value() != null
+ && myObject.value() > Long.valueOf(Instant.now().getEpochSecond())) {
+ // ...
+}
+Why the hell do I need to specify types?
+As you saw in my previous examples, Java requires you to specify the data type when defining a variable. +While in Typescript, a definition with a value looks like
+const string = functionThatReturnsAString();
+in Java, it requires an additional specification
+String string = functionThatReturnsAString();
+This gets particularly strange, if you have a value that needs to be explicitly converted to a string:
+String string = functionThatReturnsAnObject().toString();
+When the function is already specifying a return type, specifying the type of the variable seems to be just overhead. +The compiler could just infer the type automatically!
+But to improve the situation in Java 10 and higher, instead of the data type, one can use the var
keyword (which is rather a reserved type name, to be exact), so that it looks similar to TypeScript:
final var string = functionThatReturnsAnObject().toString();
+The Java compiler then also automatically infers the actual type.
+In fact, TypeScript also has a var
keyword, though I never would use it, and instead only use const
or - in rare cases - let
.
+Differentiating between variables that can change later (let
) and those which may not be changed (const
- an immutable value) is a very useful feature to avoid unwanted changes.
Union types
+A nice feature of TypeScript are Union Types. +They allow to combine multiple types to be used in a clear way:
+type Fruit = "apple" | "orange" | "banana";
+type DairyProducts = "milk" | "butter";
+
+type Food = Fruit | DairyProducts;
+There is no such thing as Union Types in Java.
+In Java, you would instead use enum`s for defining `Fruit
and DairyProducts
.
@AllArgsConstructor
+public enum Fruit {
+ APPLE("apple"),
+ ORANGE("orange"),
+ BANANA("banana");
+
+ String name;
+}
+The upper case identifiers work as constants, the string literals in the braces the values of these constants.
+The field name
is needed to hold the value in each of the three instances of Fruit
.
+Fruit values can be accessed like this:
Fruit fruit = Fruit.BANANA;
+However, it is not easily possible to combine such enums, because they are compiled to constants.
+Instead, you would use interfaces and let the Fruit
and the DairyProduct
enums implement this interface.
public interface Food {}
+
+@AllArgsConstructor
+@Getter
+public enum Fruit implements Food {
+ APPLE("apple"),
+ ORANGE("orange"),
+ BANANA("banana");
+
+ String name;
+}
+
+@AllArgsConstructor
+@Getter
+public enum DairyProduct implements Food {
+ MILK("milk"),
+ CHEESE("cheese");
+
+ String name;
+}
+Now you can use the new Food
interface as the type to collect both, `Fruit`s and `DairyProduct`s together:
List<Food> food = List.of(Fruit.BANANA, DairyProduct.CHEESE);
+Note that it is not possible (as far as I know) to use Food
as the qualifier for BANANA
and CHEESE
and that it is not that easy to use the lower case equivalents for assignments.
+Instead, one would need to iterate over the enum values and find
the requested value text.
I use Lombok to not have to implement getters and constructors. +They are necessary to have the lower case values at least when using the enum for writing to a database or when generating JSON.
+Also note that every interface and class needs a separate file in Java.
+All in all, `enum`s seems to be a very cumbersome feature. +But this is also valid for TypeScript, where I prefer to use Union Types.
+Combine object structures
+To combine object structures in TypeScript you would use the &
:
type Person = {
+ name: string;
+ email: string;
+};
+
+type AuthDetails = {
+ username: string;
+ password: string;
+};
+
+type User = Person & AuthDetails;
+In Java, you would rather define two interfaces, and define a class implementing both. +This would work in Typescript as well. +However, interfaces in TypeScript can only be used to describe object strucures, not primitives. +Read more about the differences of type aliases and interfaces in the official TypeScript documentation.
+I’m still working with Java, so stay tuned to read more experiences in the next chapters of my journey, when I will cover optional values, defaults and mapping of objects.
++ typescript java +
+ + + + + + + +Feedback
+Was this page helpful?
+ + ++ Glad to hear it! Please tell us + how we can improve. +
++ Sorry to hear that. Please tell + us how we can improve. +
+