Hey, I warned you it was going to be contrived. But it's
really not uncommon to find stuff like this in real Java
code.

The problem here is that even if validation of the input
parameters (in the elided parseDate() method) fails, we
still receive an instance of Period. But the Period we
get isn't actually in a "valid" state. What do I mean by
that, precisely?

Well, I would say that an object is in an invalid state if
it can't respond meaningfully to its public operations. In
this case, getStartDate() and getEndDate() can throw an
IllegalStateException, which is a condition I would
consider not "meaningful".

Another way to look at this is that what we have here is a
failure of type safety in the design of Period. Unchecked
exceptions represent a "hole" in the type system. So a more
typesafe design for Period would be one which never uses
unchecked exceptions—that doesn't throw
IllegalStateException, in this case.

(Actually, in practice, in real code, I'm more likely to
encounter a getStartDate() which doesn't check for
null, and actually results in a NullPointerException
further down the line, which is even worse.)

And, of course, this code suffers from the same problem as
the original Java code. The two assertions are screaming
at us that there is a problem with the typesafety of the
code.

Making the Java code better

How could we improve this code in Java. Well, here's a case
where Java's much-maligned checked exceptions would be a
really reasonable solution! We could slightly change Period
to throw a checked exception from its constructor:

Now, with this solution, we can never get a Period in an
invalid state, and the code which instantiates Period is
obligated by the compiler to handle the case of invalid
input by catching the DateFormatException somewhere.

This is a good and excellent and righteous use of checked
exceptions, and it's unfortunate that I only rarely find
Java code which uses checked exceptions like this.

Making the Ceylon code better

What about Ceylon? Ceylon doesn't have checked exceptions,
so we'll have to look for a different solution. Typically,
in cases where Java would call for use of a function that
throws a checked exception, Ceylon would call for the use
of a function that returns a union type. Since the
initializer of a class can't return any type other than the
class itself, we'll need to extract some of the mixed
initialization/validation logic into a factory function.

Or, if we didn't care about the actual problem with the
given date format (probable, given that the initial code we
were working from lost that information), we could just use
Null instead of DateFormatError:

At least arguably, the approach of using a factory function
is superior, since in general it obtains better separation
between validation logic and object initialization. This is
especially useful in Ceylon, where the compiler enforces
some quite heavy-handed restrictions on object
initialization logic in order to guarantee that all fields
of the object are assigned exactly once.

Summary

In conclusion:

Try to separate validation from initialization, wherever
reasonable.

Validation logic doesn't usually belong in constructors
(especially not in Ceylon).

Don't create objects in "invalid" states.

An "invalid" state can sometimes be detected by looking
for failures of typesafety.

In Java, a constructor or factory function that throws a
checked exception is a reasonable alternative.

In Ceylon, a factory function that returns a union type is
a reasonable alternative.