1

I am working on a project that requires the use of a large number of objects of classes extending a particular abstract class. Instantiating manually requires a call to each object's individual constructor, which is starting to pollute my client class.

The raw code from the project is too lengthy to post on this site, so I have a simplified example below:

Suppose there is an abstract class called Letter:

public abstract class Letter{
    String letter;
}

And 26 individual letter classes that extend the Letter abstract class:

public class A extends Letter{
    this.letter = "A";
}

public class B extends Letter{
    this.letter = "B";
}
... and so on

This is where I seek help - There is a client class called Alphabet:

public class Alphabet{
    public List<Letter> getLetters(){
        List<Letter> letters = new ArrayList<>();
        A a = new A();
        B b = new B();
        C c = new C();
        list.add(a);
        list.add(b);
        list.add(c);
        ...the list goes on

        return letters;
    }
}

As you can see, it is not ideal to instantiate each letter manually. I am looking for a way to create a list of class objects dynamically at runtime.

Perhaps something similar to this:

public class Alphabet{
    public List<Letter> getLetters(){
        List<Letter> letters = getAllClassesExtending(Letter.class);
        return letters;
        //list would then contain A,B,C,D,E,F,G, etc.
    }
}

I ultimately want to be able to create a new class and have it automatically added to a list of objects in the client class, without needing to modify the client class (e.g. I do not want to explicitly reference each letter class in the client class).

Thicc
  • 195
  • 1
  • 1
  • 5
  • 2
    Why do you think you need 26 different classes? Why not just use the `Letter` class, but make a constructor where you pass in the letter? – Dawood ibn Kareem Nov 20 '18 at 18:48
  • The use/case for having a class per letter of the alphabet remains unclear or better said strange. But, if you need this, reflection is the way to go. Needs still an external project, with built-in methods it's afaik impossible - you need to scan the classpath – UninformedUser Nov 20 '18 at 19:00
  • Maybe using `ServiceLoader` is a viable option? See also https://stackoverflow.com/questions/10304100/serviceloader-to-find-implementations-of-an-interface – Daniele Nov 20 '18 at 19:50
  • The letter scenario serves only as an illustration of my problem. Yes, there are much better ways to do the letter implementation, but my real project involves many subclasses that contain unique code that can't simply be 'passed in' by a constructor. – Thicc Nov 20 '18 at 19:56
  • Is there anything I can put in the subclasses themselves so that they can add themselves to the list at runtime? Maybe an initializer? – Thicc Nov 20 '18 at 20:25

2 Answers2

1

This sounds like a typical use case for an enum. The constants of an enum can have a class body with code, overriding methods of the base type, and even declare new fields.

The task of getting an instance of all subtypes implies a closed set of subtypes, which matches the behavior of enum types.

public enum Letter {
    A {
        @Override
        public boolean isVowel() {
            return true;
        }
    },
    B, C, D,
    E {
        @Override
        public boolean isVowel() {
            return true;
        }
    },
    F, G, H,
    I {
        @Override
        public boolean isVowel() {
            return true;
        }
    }, J, K, L, M, N,
    O {
        @Override
        public boolean isVowel() {
            return true;
        }
    }, P, Q, R, S, T,
    U {
        @Override
        public boolean isVowel() {
            return true;
        }
    }, V, W, X, Y, Z;
    ;
    String letter = name();

    public static List<Letter> getLetters() {
        return Arrays.asList(values());
    }

    public boolean isVowel() {
        return false;
    }
}

This demonstrates that overriding methods is possible and also a method for getting all instances (as an array) comes for free. Further, the names are specified intrinsically without redundancy.

If you want to have an open set of subclasses instead, to be potentially extended after compiling the code, you should look at the Service Provider concept and the ServiceLoader class. This mechanism will not automatically find subclasses but only those declared as service providers either within a file in META-INF or through the module declararation, on the other hand, it provides you with a robust, established solution that will be maintained by the Java developers or even evolve with the platform, like the integration into the new modules concept.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
0

You can use Reflections library to find the classes in runtime, e.g. if all of them are located under com.letters package and have a no-arg constructor:

public List<? extends Letter> getLetters(){
  Reflections reflections = new Reflections("com.letters");
  Set<Class<? extends Letter>> classes = reflections.getSubTypesOf(Letter.class);
  return classes.stream()
        .map(clazz -> {
            try {
                return clazz.newInstance();
            } catch (InstantiationException | IllegalAccessException ex) {
                throw new RuntimeException(ex);
            }
        })
        .collect(Collectors.toList());
}

It's not the most efficient way to lookup classes, you might want to cache the results if this code is on your critical path.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
  • I'm having trouble installing the Reflections library in my Java project. Is there a jar or zip with all of the compiled classes? (using maven is not currently an option for me). – Thicc Nov 21 '18 at 17:39
  • @Thicc you can take a look at [reflections pom.xml](https://search.maven.org/artifact/org.reflections/reflections/0.9.11/jar) to figure out transitive dependencies of the library. It is however a mundane task. – Karol Dowbecki Nov 21 '18 at 17:48