2

Given:

class PhysicsClass {
    private PhysicsInstructor instructor;
    private Set<PhysicsStudent> students;
}

class ChemistryClass {
    private ChemistryInstructor instructor;
    private Set<ChemistryStudent> students;
}

class MathemeticsClass {
    private MathemeticsInstructor instructor;
    private Set<MathemeticsStudent> students;
}

Is it possible to collect all of these classes in one generic class like:

class ScienceClass<T> {
    // What to write here?
    // What to write here?
}

If this is not possible, then how can I prevent the code duplication here?

I thought of making it like:

class ScienceClass<T extends Instructor, S extends Student> {
    private T instructor;
    private Set<S> students;
}

but this would allow me to make something like:

ScienceClass<PhysicsInstructor, ChemistryStudent> scienceClass;

which would be incorrect.

Utku
  • 2,025
  • 22
  • 42
  • 1
    So far, you haven't really shown much duplication--what is similar and different between the instructor and student classes? – chrylis -cautiouslyoptimistic- Apr 05 '17 at 19:20
  • As a note, you really shouldn't name your class Class. There is already a `java.lang.Class`, and your class would shadow it, which will be very confusing. Generally speaking, I'd avoid any of the common JDK classes, including the ones in java.lang, java.util, java.io, and java.util.concurrent... and possibly others, though those are the four that really jump to mind. – yshavit Apr 05 '17 at 19:23
  • 1
    @chrylis Currently not much, but they might get different in the future. If they were different indeed, how could I (or could I) collect them into one generic class as I have shown? – Utku Apr 05 '17 at 19:24
  • 3
    Probably a contrived example, but you seem to be making classes based on data, and not behaviour. Type count can explode quickly. It might be better just to stick with a field in `Instructor` and `Student` that specifies what science class they belong to. – Jorn Vernee Apr 05 '17 at 19:28
  • Why do you have a MathemeticsStudent ? I would perhaps expect a Student, and then they could attend multiple classes – Brian Agnew Apr 05 '17 at 19:30
  • @Utku I'm wondering whether you have decided to use Java Classes to represent different School Classes in your curriculum management software just because they share the name. I would say than in a real curriculum management software different school classes would be represented by objects of the same Java Class (say SchoolClass). – Valentin Ruano Apr 05 '17 at 20:13
  • It's not "Mathemetics" unless the topic makes you puke, it's "Mathematics". – Lew Bloch Apr 05 '17 at 22:10

3 Answers3

7

Start with Interfaces for the subjects

public interface Subject {}

public interface Physics extends Subject {}
public interface Chemistry extends Subject {}
public interface Mathematics extends Subject {}

Next

public class Instructor<T extends Subject> {
  ...
}

Then, you could create just one class for your "class"

public class SchoolClass<T extends Subject> {
  Instructor<T> prof;
  ArrayList<Student<T>> students;
}
Utku
  • 2,025
  • 22
  • 42
ControlAltDel
  • 33,923
  • 10
  • 53
  • 80
  • this allows for a chemistry instructor to have a class of mathematics students – fps Apr 05 '17 at 19:31
  • 1
    @FedericoPeraltaSchaffner I have updated my answer based on your helpful comment – ControlAltDel Apr 05 '17 at 19:38
  • @ControlAltDel What if the student classes can't be generified? That is, what if the `PhysicsStudent` has a field which does not exist in other `Student`s and hence, it is not possible to generify student classes? – Utku Apr 06 '17 at 14:22
2

Given your example, the most natural thing to do would be to start with something like:

public interface Subject

public interface Instructor<S extends Subject> { ...

public interface Student<S extends Subject> { ...

The above would for example allow to do things like:

public class Whatever<S extends Subject> {
  private T<S> instructor;
  List<S> students;

The other point here: always remember the good old "prefer composition over inheritance". Meaning: be really careful about creating various different classes here and complicated inheritance settings.

Finally: of course one has to understand what the programming language offers; but: the core part of your object/class model is: to be a helpful abstraction of reality. Meaning: don't focus too much on how to use generics in the first place. First you should carefully look at the aspects of real world that your application has to deal with. Then build your solution around that!

GhostCat
  • 137,827
  • 25
  • 176
  • 248
2

Assuming that you want the two types to "match up", you can do something like this:

interface Subject {}

interface Instructor<T extends Subject> {}

interface Student<T extends Subject> {}

With our basic interfaces set up, we can now create implementations of the subjects:

class Physics implements Subject {}

class Chemistry implements Subject {}

What you put in these classes is up to you, technically you can even leave them empty. Next thing, define the generic class representing a school class.

class SchoolClass<T extends Subject> {
    private Instructor<T> instructor;
    private Set<Student<T>> students;
}

And there's your link between the two fields, as they both refer to the same T type parameter.


As you can see from this, the design still isn't very clean, particularly the pointless Subject implementations stick out.

At this point it is a tempting, but obviously bad idea to do something like this:

//PLEASE DON'T DO THIS!
interface Instructor {}
interface Student<T extends Instructor> {}

Hooray, we've avoided that ugly Subject interface, but we've created what looks like a false relationship between student and instructor.

The moral of the story is (and this really depends on your actual use case) that inheritance&generics probably isn't the best tool to tackle this problem.

biziclop
  • 48,926
  • 12
  • 77
  • 104
  • 1
    "moral of the story is ... that inheritance&generics probably isn't the best tool to tackle this problem". Then what is? I know that in order to answer this question, I should show the implementations of my classes but they are currently kind of hypothetical, hence, not much different. – Utku Apr 05 '17 at 19:44
  • 1
    That's the million dollar question, there is genuinely no one size fits all solution. Maybe the differences between different types of instructor/student can be captured by composition (or simply different values to fields), maybe you can even use something like the strategy pattern. And there are also situations where generics isn't an option at all, for example if an instructor or student can be associated with multiple subjects (as it happens in real life too). – biziclop Apr 05 '17 at 19:47
  • If an instructor is associated with multiple subjects, then I should make `Instructor` a concrete class and hold a `List` in `Instructor`, right? Or is there another way? – Utku Apr 05 '17 at 20:05
  • @Utku Yes, that's pretty much the way to go then. – biziclop Apr 05 '17 at 21:12
  • Actually in my case, `Instructor extends Student`, because instructors are chosen among the students. And the problem is, it is not possible to generify students because `PhysicsStudent` has some fields that are not present in other students. What should I do in that case? I would like to ensure that in a `ScienceClass`, types of the `Instructor` and `Student` match. – Utku Apr 06 '17 at 14:19