Java has gotten strange

home

 

My recollection of Java was it was a simplified C++. The big simplification was getting automatic garbage collection by splitting into Value and Reference types. Then it does other things like cutting out call-by-reference and operator overloading. I didn't love it, but it seemed fine.

When I looked at Java again after years I was surprised at how weird it got, or about things it always had that I didn't notice.

Misc

I'll start with minor things that don't seem very Java-like to me:

Main entry

I knew this but forgot how silly it was. Java's main entry point is main(string[] Args). Fine so far. But it must be inside of a dummy class, named the same as the file it's in:

// file: jtest.java
import java.io.*; // typical leading imports

public class jtest {  // <- "jtest" is the required name
  public static void main(String[] ags) throws IOException {
    System.out println("main entry)");
  }
}

namespaces

I forgot what a rigid structure Java imposes on files. Each public class must be in a file by itself, named the same as the class. If it's in a namespace (which Java calls packages) it must be in a directory with that name. Here's the filsystem required to have a Dog and Cat class in an Animals namespace:

// a directory with 2 files:
Animals // everything in the Animal namespace must be here:
  Dogs.java // Dog class, plus non-public dog-using classes
  Cats.java // Cat class

Then here's the cat file. despite being in the required Animal folder, it needs to declare itself as being in Animals in the first line. But at least it can have any number of private helper classes:

There's more. If you don't start with package pckgName (which effectively is your namespace) Our Dog class would need package Animals;. To sum up, a sample cats.java file:

// Cats.java:
package Animals; // must be Animals, since we're in that directory

public class Cats { ... } // class must be named Cats, to match filename

// non-public, so naming rules don't apply:
class CatHelper1 { ... }
class CatHelper2 { ... }

Things don't need to be in a package. Without package xyz; on top they goes into the global namespace. Those files can be ... anywhere. Arrgg!!

Java wants everything to be a class. I thought C# invented that nonsense, but it was Java. That means Java can't have true global functions or variables. But they do anyway, using an ugly hack through a fake static class:

// this class is really a namespace:
final class catUtils {
  public static int oldestCat=24; // global variable
  
  public static bool checkCats(Cat c1, Cat c2) { ... } // global function

  // a hack to be sure this isn't a real class:
  private catUtils() {} // private constructor prevents "new"  
}

To make it more official, a final in front prevents inheritance and a private constructor prevents creating an instance. But those make it look more like the "pretend this class is a namespace" hack that it is.

And it gets worse. We can't import this class the usual way. We need an extra static:

import Animals.Cats; // cat class
import Machines.*; // everything in machines package

import static catUtils.checkCats;  // that function from catUtils

Java private and public work the usual way. But this is weird -- the default scope is package level. Weirder, to me, you cna't even make that explicit. For example:

class Cat {
  private String name; // this is private to the class
  public double weight; // public to anyone

  int socSec; // not private or public -- package scope
  // is there a formal way, like "pkg int socSec;"? No
  
 }

c1.socSec=1; // legal -- package scope & same file

Classes

Every language has slightly different syntaxes for funny class stuff. I forgot how old and weird Java's is. To subclass use extends. To have one constructor call another, use this(). To call an overwritten function from your parent use super:

class Cat extends Animal { // subclass using extends
  
  // calling Animal constructor with super:
  public Cat(int age, String name) {
    super(age,name);
    ..
  }
  
  public Cat() {
    // calling another constructor using this()
    this(0,"");
  }

  // also call an overriden function with super:
  public double cost() {
    double baseCost=super.cost();
  }
}

One more fun way to use super is with inherited variables. If Cat inherits name from Animal, Cat's are allowed to call it super.name (or name or this.name). That seems ... pointless.

It's great how every function in Java is automatically virtual. If you want to override in a subclass, just do it -- no keywords required. It's a great system. Python uses it (sort of). But then Java messed it up.

The problem they wanted to solve was mispelling an attempted override. If you attempt to override eat() but misspell it eet() there's no error, no warning -- but nothing will happen. A solution, used by languages older than Java, is the override keyword. Now override eet() gives a "nothing to override" error. But Java didn't want to add a keyword. Instead the "suggest" you use a decorator:

  @override
  public void goLeeft() { ... }
  // helpful compile error "no such function to override"
  // but @override is optional

Java introduced the idea of preventing inheritance from a class, by adding final in front. Or use final to prevent a function from being overriden:

// can't sublass a Dog:
final class Dog extends Animal {

  // Dog.eat can't be overridden:
  // (which is would only matter if Dog could have subclasses)
  public final void eat() { ... }

That seems so quaint now. Java was at the peak of the OOP movement when coders wanted to subclass anything. Today all of those final's seem silly -- who are all of these people trying to subclass from you? It's extra funny how C# stole the idea but changed the name to an equally unhelpful sealed.

interfaces

Java started the idea of eliminating multiple inheritance, replacing it with requirements-only interface classes. The rules for writing an interface are fine:

// pure abstract classes anyone can inherit from:
public interface buyable { double cost(); }
public interface talks { string mySound(double anger); }

Then it gets a little odd. Inheriting from a real class or from an interface is the same concept. You don't need to know which is which, but the keyword is different (implements):

class Cat extends Animal implements buyable, talks {
  ...
  // while we're here, implement the required functions:
  public void petFor(double timeSecs) { ... }
  public string talk(double anger) { ... }
}

Not a huge deal, but these are the same people who re-used final for const, to reduce the number of keywords.

This struck me as odd: interfaces can have static variables. Declare them, in the interface, like normal variables:

public interface buyable {
  double cost();
  int maxCost; // a static
}

// using it. It's really a static:
buyable.maxCost=99;

Nested classes get free backpointers

Instances of a nested class need to be created "from" some instance of the enclosing class. This is so they can get a free hidden backpointer to the "parent" instance. Pretty cool and useful. An example where a Forest has Trees which automatically know which forest they are in:

class Forest {
  public int temperature;
  
  void makeATree() {
    Tree t1 = new Tree();
    // the new tree automatically backlinks to this Forest
    ...
  }
  
  // nested Tree class:
  class Tree {
    public grow() {
      // using the temperature of this Tree's forest:
      double n = temperature/10; // <-- Wow!
    }

Whoa! Inside Tree's grow function we use Forest's temperature like it was a global. That's a classic rookie mistake, except it's legal in Java. As a bonus, you're allowed to explicitly follow your Forest link with an odd syntax:Forest.this.temperature. In languages without a free backlink we're constantly adding Forest myForest; then using myForest.temperature.

But this is weird -- what if a non-Forest member function creates a Tree. That Tree requires a secret backlink to some Forest. Java forces us to supply one:

Forest f1 = new Forest();
Tree t1 = f1.new Tree(); // <- special syntax to specify the owning forest

It gets weirder. Unlike Scala (a version of Java), there's no enforcement that a forest only use its trees. Here forest#2 plays with a tree belonging to forest#1:

// tree1 links back to Forest f1:
Forest.Tree t1 = f1.new Tree();
// but Forest f2 can use it:
f2.useTree(t1);

Java gives us one more funny rule for this. Suppose you want to nest a regular class -- one that has no reason for a backlink. You can by writing static in front, as in static public class Tree. That's weird for such a minimalist language. If you don't want a free back-pointer, don't nest the class.

Auto-Boxing

When Java was made it seemed obvious that Value and Reference types was the best compromise. Value for small things is fast, while Reference for larger classes allows useful pointers. Then other languages came along and showed how "everything is a reference" can make things easier. An obvious trick turning Value types into References is creating creating a 1-item class. This is known as "boxing" (we put the Value type into a Reference-type box):

class IntHolder {
  public int n;
}

IntHolder num1 = new IntHolder();
num1.n=7;

That works, but it's a pain, so Java invented auto-boxing. It works pretty well. They use Integer, Float, Double, Character, and Boolean and classes invisibly holding 1 item of that basic value type. Here's Integer in use:

// Integer is a class:
Integer n1 = new Integer(5);
Integer n2 = 9; // shortcut
int n3 = n1; // also a shortcut
// note how the types are different: int = boxed int
 
n1=null; // fine, since a reference
n3=null; // nope. Error

It's pretty seemless. But at the same time, it's awfully complicated to remember we now have 2 types in integers and so on.

Exception Handling

Java requires that a function list every possible error it might cause. Wow. It's not that bad since you're allowed to use try-catch inside. Ideally you catch every possible error and don't need to junk up the function header -- no errors can be thrown out of it. But otherwise you'll need throws and a list of error codes. Yikes:

// this attempts to open a file, which may fail:
public void doStuff() throws IOException {

  // we catch bad indexes, otherwise they'd be listed above:  
  try {
    int n=A[x]; // errors here will be caught by us
    ...
  }
  catch(IndexOutOfBounds err) { ... } // handle it 
  finally { ... }
  // finally is optional...
  // ...it always runs, even for uncaught exceptions

Mutltiple exceptions in a catch strangely combine with | (a vertical bar). I realize there's no existing "combine types" operator, but this feels janky:

  try {
    ...
  }
  catch(IOException | NullPointerException e1) { ... }
  catch(OutOfMemoryError e2) { ...}
  // mutiple catches are allowed 

But all-in-all it's a nice idea, but maybe not explained well. In Java you can handle all of your own errors, like in a normal program; or you can choose not to as long as you tell callers which errors they have to worry about.

Serialization

Ah, yes! One of the main reasons to use Java is that it can automatically save and restore variables to JSON or whatever. Then it gets weird. Java's built-in serialization is considered not-so-good; everyone uses some 3rd party library. And not the same one.

Adding implements Serializable lets most Java classes serialize themselves. It will even follow references and serialize those. That fails horribly when you refer to items "owned" by someone else. But you can add transient and re-construct it yourself later. Ex:

class savableCat implements Serializable {
  // this will be serialized:
  CatCollar myCollar;

  // points to an item in a master list. so not serialized:
  transient CatCollar myDreamCollar;
  // Arg. We need a way to restore myDreamCollar:
  int myDreamCollarID;  // this will be serialized
}

So it's great for simple stuff, but for anything complex it's no magic bullet. Actually even normal serialization is a pain:

boring serialization details

To serialize you need to create an ObjectOutputStream, backed by a file or a byte output stream, then write your object to it:

Dog d = new Dog(); // set dog values

ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
ObjectOutputStream byteWriter = new ObjectOutputStream(bytesOut);

// byteWriter can write any class, writing one dog now:
byteWriter.writeObject(d); // writes out Dog d to the backing bytesOut

byte[] dogS = bytesOut.toByteArray(); // convert to normal byte array
f.write(dogS); // finally to a file

Reading back is the reverse process, from a file or byte array:

ByteArrayInputStream bytesIn = new ByteArrayInputStream(dogS);
ObjectInputStream dogReader = new ObjectInputStream(bytesIn);
// dogReader is ready to read whatever is in dogS

// pull out whatever's there. It will be an "Object", but we know its a Dog:
Dog d2=(Dog)dogReader.readObject();

Threads

Adding synchronized in front of any function limits access to one thread at a time:

public int synchronized getSomething(...) { ...}

Container classes have synchronized versions (all of their functions automatically start with synchronized, I think.

You can't declare an object to be synchronized. Instead each has a built-in lock. synchronized(a1) {} either gets a1's lock, or waits until it's free. The lock is auto-released st the end:

synchronized(c1) { // wait for and acquire c1's lock 
  // no one else can use c1 until we finish
}

That's not too bad. It's just a built-in mutex variable.

Of course, synchronized is for threads. Making Java threads is a pain. First create a class inheriting from java.lang.Thread. Overload the run() function. Run it with new Thread(new myNewThread()).start(); (why start? Because start runs run(), obviously). Here's an example where 3 identical threads slowly count to 10. With a synch, they run in sequence. Without, the numbers from each thread will interleave:

Long thread-using code
class threadRunner {
  // main entry:
  public void runThreeThreads() {
	    threadTest t1 = new threadTest(); // t1 is ready to be spawned
	    threadTest t2 = new threadTest();
	    threadTest t3 = new threadTest();
	    
	    new java.lang.Thread(t1).start(); // starting the threads
	    new java.lang.Thread(t2).start();
	    new java.lang.Thread(t3).start();
        // they will now all count 1 to 10
	  }

  // nested class for the Tread (so we can synch on the main instance)
  class threadTest extends java.lang.Thread { // <- actual Thread
  
    public void run() { // <- the required "run" function
      // manually synch on our common backpointer:
      synchronized(threadRunner.this) {
        // print 1 to 10 with a delay between #'s"
        for(int i=0;i<10;i++) {
          for(int j=0;j<1000000;j++) {} // delay
            System.out.println(i);
        }
      }
    }
  }
}

Container types

I vaguely recall some crazy Java containers in the past, but looking at them now, they're fine. One weird part are how everything must be an Object -- int's are autoboxed into Integer and so on. Another is how, since Java has no operator overloading, you can't use A[0] for any of the lookups. The boringly-named ArrayList is a standard list implemented as an array. It lives in the java.util package. It has the usual operations:

import java.util.ArrayList;

ArrayList<String> A1 = new ArrayList<String>();
A1.add("cow"); // to end
A1.add(0,"moo"); // inserting before index 0

A1.size(); // 2 (why is it called size? Oh, well)

A1.remove(1); // by index
A1.remove("cow"); // search for item

remove seems confusing since it takes an int index or an object to search for. The secrete is you can't have an ArrayList of regular int's, only the class Integer. That seems error prone to me, but oh well.

Instead of A[0]. We have a set and a get. Yeesh:

A1.set(0,"bear");  // A1[0]="bear";
String w = A1.get(0);  // w=A1[0];

A little stranger: the thread-safe ArrayList. is named Vector.

LinkedList is the linked list type. It has multiple identical functions: addLast, push, and offer add an item to the end. peek and poll get the value of the first element. This allows you to pretend a LinkedList is a stack or a queue (or whatever uses poll).

LinkedList isn't synchronized. There's no synchronized version, but you can use SynchronizedCollection(myLL) to make one (I think. It's not pretty).

Java's old-fashioned fixed-length array is pretty normal. Still always an object, gets to use [], and has the only Java "property" with dot-length:

Cat[] C = new Cat[8]
C.length;  // Sigh...no parens, the only place
C[2]=new Cat();  // [] only for on "real" arrays

The basic hash-map is HashMap. It also can't use H["cat"]=5;. Instead we have put and get:

// note how we need to use Integer, which is a reference type:
HashMap<String, Integer> MaxAge = new HashMap<String, Integer>(); 

MaxAge.put("cow",17); // instead of MaxAge["cow"]=17
int xx = MaxAge.get("cow"); // 17
// note: Integer to int is auto-unboxing

First class functions

Java can now fake standard function passing. This sorts cats by age using what looks exactly like an anonymous function:

// use built-in sort to arrange cats by age:
C.sort( (c1,c2)-> c2.age-c1.age );
// NOTE: age1-age2 is a hacky <0 / 0 / >0 compare function

Below, Predicate<Cat> acts just like a function pointer, taking a Cat-to-boolean, set to another anonymous function:

import java.util.function.Predicate;

Predicate<Cat> isKitten = c -> c.age<3;

We also can pass isKitten into a function in a normal-looking way. This uses it to get part of a list:

YoungCats = sublist( C, isKitten );

public ArrayList<Cat> sublist(ArrayList<Cat> C, Predicate<Cat> useIt) {
  ArrayList<Cat> C2 = new ArrayList<Cat>();
  foreach(Cat c : C) 
    if( useIt.test( C ) )
      C2.add(c);
  return C2;
}

That looks normal except for if(useIt.test( C )). What is the member function call test? How does useIt even have member functions? It turns out we've been creating and passing classes, and not functions, all along. Yikes! Predicate is a class and we've been secretely making a subclass. Double Yikes!

It turns out Java was made to be fully OOP. It doesn't really have first-class functions. For real it only uses the OOP Command design pattern. It's very complicated. First we need an abstract single-function class. It will eventually hold a real function. We'll assume we want one converting a String to an int:

// abstract base class:
interface stringConverter {
  int value(String w);
}

Anyone can take that base class as input and call its function:

void processString(String w, stringConverter cnvt) {
  ...
  // cnvt is a class, calling its value member function:
  int x = cnvt.value(w);
  ...
}

To supply any function we choose, we create a subclass:

// subclass:
class zPos implements stringConverter {
  // implementing our function as the single "value" function:
  public int value(String w) { return w.indexOf('z'); }
}

// we need to create an instance to pass it somewhere:
processList(L, new zPos());

This is a huge pain. To make it simpler, Java created rules for anonymous subclasses. Here's the formal way to sort cats by age:

C.sort(
  new java.util.Comparator<Cat>()
    {
      public int compare(Cat c1, Cat c2) { return c1.age-c1.age; }
    }
);

The () { ... } syntax creates an anonymous subclass of Comparator<Cat>. Comparator is the base class sort expects. It has a single function named compare.

When you supply only a "lambda function", as in sort((c1,c2) ->c1.age-c2.age), Java converts it to anonymous subclass syntax. It's really quite clever.

Or consider Function<String,Int> f1 = (w)->w.length();. Function is actually an abstract class (with a single member -- apply). Java uses (w)->w.length() to create an anonymous Function subclass. Anyone using f1 would use f1.apply(w).

Here's one more of those, using a base class we create:

interface DtoD { double convert(double d); }

// creates an anonymous subclass of DtoD:
DtoD tripler = (n) -> n*3;

ff(4.5, tripler);  // computes 13.5

void ff(double d, DtoD mutator) {
  // calling the convert member function:
  double d2 = mutator.convert(d);
  ...
}

Abstract classes such as DtoD are required, but it's a pain making them. So Java provides several. Predicate takes anything to bool. Function converts 1 input into any a bool output (as an exercise: Predicate<Double> is the same as Function<Double, boolean>). UnaryOperator converts an input to an output of the same type. Consumer has 1 input and no output. IntConsumer is a shortcut for Consumer<Integer>. Supplier is the reverse -- no input, 1 output. BinaryOperator has 2 inputs and 1 output, all of the same type, while BiFunction can have 3 different types.

What a mess. C# managed to do it with just Func (anything returning a value) and Action (anything with no return value.

But there's more. Your abstract base class is allowed to have extra functions as long as you clearly mark which one the shortcut will fill in. Mark the extras with default:

public interface buyable {
  double cost(Cat c); // <-this one is auto-overloaded
  default boolean tooExpensive(Cat c) { return cost(c)>10; }
  default String comment(Cat c) { return "wow!"; }
}

buyable allMustGo( (c)-> 0.1 );  // this will be buyable.cost

To sum up, passing functions in Java is now almost completely normal. But only through the slickest of hacks on Java's real system, which is a mess.

Java's system lives on in C#. sort can take a function as input, but it can also take a subclass of the Comparator interface.

enums

Basic Java enums work as usual:

// basic enum:
enum color { RED,GREEN,BLUE; }
color c1 = color.RED; // note how the type goes in front

Then Java goes further and allows you to add extra data to enums, which is normally a scripting language trick. The extra data goes in parens after each enum value:

enum color {
  // extra data goes in parens after each:
  RED(3.2), GREEN(4.7), BLUE(6.8);

  // the name for each color's value:
  public double prettyness;  
  // auto-run constructor sets prettyness for each color:
  color(double val) { prettyness=val; }
}

color c1=color.RED; c1.prettyness; // 3.2
color.BLUE.prettyness; // 6.8

Java actually creates each color as an instance (just as simply an int). The funny stuff with prettyness and the constructor is about naming and installing the values (3.2, 4.7 and such) into each enum item. Use it like color.RED.prettyness.

You can even give them member functions! We could add bool isPretty() { return prettyness>5; } then use it with if(c1.isPretty()).

Bizarrely, these extra values don't need to be const/final. Each color can change it's prettyness: color.BLUE.prettyness=14; is fine. It affects every reference to color.BLUE (obviously, since they all point to the same object).

Templates (Generics)

Java uses the restrictive form of template classes: the template type must be a class which you can't assume anything about. For example, oldEnough is illegal since it tries to use age (even if we only ever call it with age-having things):

static <T> boolean oldEnough(T t) { return t.age>=3; } // error -- what is age?

Notice how the template type goes in front of it all. That's different. When we use this trick for classes (such as ArrayList<T>) if goes after the name, like normal.

Templates let us use class features only if we specify a required parent class. This runs on anything inheriting from ageHaver:

class ageHaver { public int age; }

// legal age-having generic, taking any type inheriting from ageHaver:
static <T extends ageHaver> boolean oldEnough(T t) { return t.age>=3; }

That's not so useful since we could already do that using normal inheritance.

One last cool thing about templates is they don't really do anything. Everything in Java really takes just Objects anyway. For example, ArrayList<String> is a list of Objects. Java's type system only checks they have the required bits during compile time. The system is called Type Erasure.

 

 

Comments. or email adminATtaxesforcatses.com

name test