How to restrict that subclass cannot be generic?
Asked Answered
C

3

18

Compile time error: The generic class may not subclass java.lang.Throwable

public class TestGenericClass<E> extends Exception {

/*Above line will give compile error, the generic class TestGenericClass<E> may 
  not subclass java.lang.Throwable*/

    public TestGenericClass(String msg) {
        super(msg);
    }
}

Above compile time error is for the reason given in § jls-8.1.2 as below, and explained in this question:

It is a compile-time error if a generic class is a direct or indirect subclass of Throwable(§11.1.1).

This restriction is needed since the catch mechanism of the Java Virtual Machine works only with non-generic classes.

Question:

  • How it is restricted that subclass of java.lang.Throwable will not be generic class?

  • Or more generic question will be, how to restrict that subclasses of any class cannot be generic?

Captain answered 3/5, 2014 at 21:0 Comment(9)
I'm not entirely sure if they use an actual Java tool. This might just be a special case that they've included in the compiler.Mease
If that is the case, then how can I create my own custom class that will not have a Generic class.Captain
You cannot. This is done in the compiler. You can always extend the compiler and add in a rule for your class.Momently
Just have your class extend the Throwable, if you must let the compiler complain when someone tries to do that.Aryanize
@Bhesh, I suppose someone might be tempted to throw it then...Momently
@BoristheSpider please elaborate.Captain
@BheshGurung we extends to Throwable when we are creating some Exception or Error class. This will give problem if I will have to extend class like AppletCaptain
Please don't take that suggestion seriously. I don't think you have a very legit question here. May I ask you why you want to be able to do that?Aryanize
@BheshGurung I am just curious to know how things are implemented for java.lang.ThrowableCaptain
S
20

How it is restricted that subclass of java.lang.Throwable will not be generic class?

Here's how OpenJDK compiler performs the check:

import com.sun.tools.javac.code.Symbol.*;   

private void attribClassBody(Env<AttrContext> env, ClassSymbol c) {
    ....

    // Check that a generic class doesn't extend Throwable
    if (!c.type.allparams().isEmpty() && types.isSubtype(c.type, syms.throwableType))
        log.error(tree.extending.pos(), "generic.throwable");

As you can see forbidden type is kind of harcoded, so you can't use the same technique for your custom class without compiler code customization.

Full source code

Smail answered 3/5, 2014 at 21:30 Comment(5)
Can you please add class name and/or any other sources?Nonagenarian
Although hacky, you could prevent subclasses from being generic by making your base class extend throwable :)Periwig
What occurs if one tries to run code written on a compiler that skips this check, on a standard JVM?Singular
@hexafraction I would guess nothing different. The restriction is only so that catch doesn't have to support generics at compile time. (Anything generics-related is only at compile time in Java anyway.)Blanchblancha
@hexafraction the only spec for "standard JVM" I know is Java virtual machine specification by Oracle. This spec doesn't cover use case we're talking about, so I would say for "standard JVM" the behavior is undefined.Smail
M
10

How it is restricted that subclass of java.lang.Throwable will not be generic class?

This was a decision to write a special case into the compiler itself. The reason why is detailed in this question. Basically, this is to do with a type being reifiable. You can read about this term here. In short, a type is reifiable if it's type is fully available at compile time. Generic types, for example, are not reifiable, because their types are removed by type erasure. Objects appearing in a catch block need to be reifiable.

Or more generic question, how to restrict that subclasses of a class cannot be generic?

Well there's a few options..

At present, there is no option within the normal bounds of Java to do this. It doesn't have some sort of final implementation that prevents genericity being applied to subclasses. The closest thing you can get to this, as explained in the comments, is to extend the compiler and add a rule specifically for your class. This solution sends shivers down my spine. Dodge it. It means that your code will only behave with your version of Java, and that anyone else who wants to use your code will have to install the same version.

Obviously, the other option is to extend Throwable, but this really isn't a good idea. It adds a whole bunch of functionality to your class, and adds a lot of new methods to the interface of your class, that you will never use. From an OOP point of view, you're sacrificing the integrity of your class for the benefit of having this feature.

Mease answered 3/5, 2014 at 21:22 Comment(3)
@BoristheSpider Haha, sorry. I know you suggested this solution. As soon as I saw "extend the compiler", I honestly did flinch a little. No offence intended obviously!Mease
That wasn't really intended a serious solution. Writing compilers should be left to the compiler writers. An annotation preprocessor is fairly simple to do. Or even something like a Checkstyle rule.Momently
These are certainly valid options!Mease
S
1

If you're willing to delay the error until run time, you can use use reflection in the superclass constructor(s) to see if the subclass declares any type parameters.

public class Example {
    public static class ProhibitsGenericSubclasses {
        public ProhibitsGenericSubclasses() {
            if (getClass().getTypeParameters().length > 0)
                throw new AssertionError("ProhibitsGenericSubclasses prohibits generic subclasses (see docs)");
        }
    }

    public static class NonGenericSubclass extends ProhibitsGenericSubclasses {}
    public static class GenericSubclass<T> extends ProhibitsGenericSubclasses {}

    public static void main(String[] args) {
        System.out.println(new NonGenericSubclass());
        System.out.println(new GenericSubclass<Object>());
    }
}

This code prints

Example$NonGenericSubclass@15db9742
Exception in thread "main" java.lang.AssertionError: ProhibitsGenericSubclasses prohibits generic subclasses (see docs)
    at Example$ProhibitsGenericSubclasses.<init>(Example.java:12)
    at Example$GenericSubclass.<init>(Example.java:17)
    at Example.main(Example.java:21)

If you want to prohibit type parameters of all classes in the hierarchy, not just the most-derived class, you'll need to walk up the hierarchy using Class#getSuperclass().

Synthetic answered 8/5, 2014 at 6:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.