🔴 The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
// CAPTURED — OpenJDK 25 Temurin (verified byte-identical bare throw on OpenJDK 17.0.19):
// Shape.class.getDeclaredConstructor().newInstance() on an abstract class.
// Thrown WITHOUT a detail message — the bare name is the whole paste (StackOverflowError-style).
Exception in thread "main" java.lang.InstantiationException
at java.base/jdk.internal.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:483)
at com.devinhyderabad.factory.PluginFactory.create(PluginFactory.java:22)⚡ Quick Fix Works 80% of the time
Instantiate the concrete implementation, not the abstraction — map the requested type to a constructable subclass before newInstance.
Class<?> requested = Class.forName(config.get("plugin"));
if (requested.isInterface()
|| Modifier.isAbstract(requested.getModifiers())) {
throw new IllegalArgumentException(
requested.getName() + " is not instantiable — configure the impl");
}
return requested.getDeclaredConstructor().newInstance();🧠 Why this Happens
Tap to expand the deep technical explanation
Constructor lookup succeeds because abstract classes legitimately have constructors — they run while subclasses initialize. The refusal happens one step later at allocation: HotSpot checks ACC_ABSTRACT and ACC_INTERFACE flags before carving heap space and aborts for anything lacking a concrete body. Recent JDKs raise InstantiationException with NO detail message (verified identically on OpenJDK 17.0.19 and 25.0.2), which leaves the failing type invisible in logs unless your own guard names it. A concrete class with no zero-arg constructor is a different failure — getDeclaredConstructor() throws NoSuchMethodException before newInstance ever runs.
The HITEC City Parking Spot Analogy:
Ordering 'a vehicle' from a catalog page labeled vehicle (concept): the factory pours metal only for concrete models, so the order bounces before assembly starts.
🔁 How to Reproduce Confirm this is your error
Declare abstract class Shape {}, then Shape.class.getDeclaredConstructor().newInstance(). (Lab capture: OpenJDK 25 — bare exception, no message.)
🛠️ Solutions (5 Ways to Fix)
Map abstractions to concrete classes in a registry
👉 Use this when/if configuration names an interface or abstract base but your factory must produce instances.
Store the pairing explicitly and fail fast when someone configures the abstraction itself. The registry documents intent, survives obfuscation better than naming conventions, and turns a runtime surprise into a startup error.
Map<Class<?>, Class<? extends Shape>> impls = Map.of(
Shape.class, Circle.class,
Transport.class, Truck.class);
return impls.getOrDefault(requested, Circle.class)
.getDeclaredConstructor().newInstance();Store Suppliers instead of Class tokens
👉 Use this when/if constructors need arguments or you want zero reflection on the hot path.
A Map<Class<?>, Supplier<?>> replaces newInstance with a plain lambda call — the compiler verifies constructability up front, supports parameters naturally, and eliminates this exception's entire failure class.
Map<Class<?>, Supplier<Shape>> factories = Map.of(
Shape.class, () -> new Circle(10),
Transport.class, () -> new Truck("diesel"));
return factories.get(requested).get();Discover implementations with ServiceLoader
👉 Use this when/if plugins register themselves and hand-maintained registries keep drifting out of sync.
ServiceLoader loads concrete classes declared in META-INF/services files — discovery, instantiation, and error reporting follow a JDK-standard contract, so nobody hand-writes Class.forName chains that can hit an abstraction.
// META-INF/services/com.devinhyderabad.shape.Shape
com.devinhyderabad.shape.Circle
Shape first = ServiceLoader.load(Shape.class).iterator().next();Guarantee a public zero-arg constructor for bean-style APIs
👉 Use this when/if frameworks like Jackson, JAXB runtimes, or JSP useBean instantiate your class reflectively.
Bean-style machinery looks up the no-arg public constructor. Without one you see either this exception (abstract) or NoSuchMethodException (missing ctor). Adding an explicit public ClassName() fixes the framework path without touching framework code.
public Report() { // explicit, public, no-arg
this(LocalDateTime.now());
}
public Report(LocalDateTime generatedAt) { ... }Triage precisely: which reflective failure is this?
👉 Use this when/if the factory throws intermittently across differently-configured environments and you need a decision table.
Abstract/interface type → InstantiationException at allocation. Missing zero-arg ctor → NoSuchMethodException from getDeclaredConstructor. Existing-but-private ctor → IllegalAccessException from newInstance. Three exceptions, three distinct fixes — guessing wastes hours.
InstantiationException -> type itself unconstructable: use concrete subclass
NoSuchMethodException -> no ()V constructor: add one or pick explicit params
IllegalAccessException -> ctor not visible: setAccessible or make public📋 Version Notes
Class.newInstance() also throws this and sneaks checked exceptions past the compiler; its message included the type name.
Class.newInstance() deprecated for removal — Constructor.newInstance() is the supported path.
Throws the bare exception without a detail message — captured directly on OpenJDK 17.0.19; identical through Java 21+.
Behavior identical to 17; sealed hierarchies make explicit concrete-type mapping the natural design.
🛡️ How to Prevent This Next Time
Never configure abstract types where instances are required, keep factory registries Supplier-typed, add a startup unit test that instantiates every registered implementation, and document constructor requirements next to bean-style integration points.