-1

I will start this question with an example and then I will tell what I have in my mind.

public class testCommand extends commandBase implements commandTypeA{

    //this is from commandTypeA
    @Override
    public type executable() {
        //type is enum
        return type.EVERYONE;
    }

    //this is from commandBase 
    @Override
    public String name() {
        return "test";
    }
}

public class testCommand2 extends commandBase implements commandTypeB{

    //this is from commandTypeB
    @Override
    public String description() {
        return "A testCommand";
    }

    //this is from commandBase 
    @Override
    public String name() {
        return "test";
    }
}

In that example I don't want to have description in commandTypeA and executable in commandTypeA

I will create also many classes like testCommand and testCommand2. I will always extend commandBase and will implement commandTypeA or commandTypeB.

I want to store those classes into an ArrayList or just a variable (that could store both of the classes - with implemented commandTypeA or commandTypeB and all of their methods - including methods from the extended and implemented classes). Example with ArrayList:

ArrayList<*datatype*> registeredCommands = new ArrayList<*datatype*>;
registeredCommands.add(new testCommand());
registeredCommands.add(new testCommand2());

Example with variable:

*datatype* currentCommand = new testCommand();
currentCommand = new new testCommand2();

Also I need to perform actions later in my code like

Example with ArrayList:

system.out.println((commandBase) registeredCommands.get(0).name());
system.out.println((commandTypeA) registeredCommands.get(0).executable());
system.out.println((commandTypeB) registeredCommands2.get(1).description());

Example with variable:

*datatype* currentCommand = new testCommand();
system.out.println((commandBase) currentCommand.name());
system.out.println((commandTypeA) currentCommand.executable());

currentCommand = new new testCommand2();
system.out.println((commandTypeB) currentCommand.description());

My questions are:

  • Is there any type of datatype that i could use in my case?
  • Can I do the same thing I have in mind in other (better) ways?
Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95
  • 4
    How does your code know whether it can call `executable` or `description`? The answer to this question should tell you how to structure the code. In general, mixing different types with different interfaces in the same collection doesn’t really make sense. There are certainly exceptions to this but in a good design they’re rare. – Konrad Rudolph Nov 09 '21 at 12:53
  • I think PECS is very relevant here. [PECS](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super) – Shark Nov 09 '21 at 13:05

1 Answers1

3

Is there any type of datatype that i could use in my case?

That depends on the use-case but you can use CommandBase, CommandTypeA or CommandTypeB directly or as boundaries for wildcards (I changed the class names as per common convetions).

Example with variable:

CommandBase currentCommand = new testCommand();
currentCommand = new testCommand2();

This is basically "program to interface" (in this case abstract base class), i.e. use the common type. Ideally you'd introduce a Command interfact that the others extend and CommandBase implements in which case you'd use

Command currentCommand = new testCommand();

When it comes to calling, you'd need instanceof and with pattern matching (as of Java 14) you can do it like this:

if( currentCommand instanceof CommandTypeA typeA) {
   typeA.execute();
}

Without pattern matching it would be like this:

if( currentCommand instanceof CommandTypeA) {
   ((CommandTypeA)currentCommand ).execute();
}

Example with List (also an interface):

List<Command> registeredCommands = new ArrayList<>();
registeredCommands.add(new testCommand());
registeredCommands.add(new testCommand2());

You could also use wildcards but given your question I assume that's something to bother with later (as you'll have to deal with "producer extends consumer super").

Execution of methods only available in subtypes also uses instanceof:

for(Command command : registeredCommands) {
  if( command instanceof CommandTypeA typeA) {
   typeA.execute();
  }
}

Can I do the same thing I have in mind in other (better) ways?

There are very likely better ways but those depend on your requirements.

One could be to actually maintain several lists, e.g. a List<CommandTypeA> and a List<CommandTypeB>. Any command that implements both is added to both.

Another way might be to actually move the methods up, e.g. into the Command interface (introducing it in the first place already is a way to improve your code) and provide default methods:

interface Command {      
   default Type executable() {
     return null; //or return Type.EVERYONE, Type.NOBODY etc. - whatever makes sense
   }

   default String description() {
     //return any sensible default description 
     return "a command";
   }
}

Doing this you can call both methods even if you have only a Command reference.

Thomas
  • 87,414
  • 12
  • 119
  • 157