-3

If I want to pass to a constructor a Comparator that can compare on two different types, should the parameter of the constructor be like this?:

public MyClass(Comparator<?> comp) {
  this.comp = comp;
}

Then my comparator class:

public NameComparator implements Comparator<String> {

  @Override
    public int compare(String s1, String s2) {
        return s1.compareTo(s2);
    }
}

Then whenever I instantiate the class I do:

MyClass myClass = new MyClass(new NameComparator());

Is this the correct way to go about doing this?

Thanks

EDIT:

Here's the relevant code:

public class BST<T> {
  /* binary search tree */
  ...
  private Comparator<T> c;

  /* pass in comparator to constructor*/
  public BST(Comparator<T> c) {
    this.c = c;
  }

Comparator:

public class NameComparator implements Comparator<String> {
 @Override
 public int compare(String s1, String s2) {
   return s2.compareTo(s1);
 }
}

When I am creating BST:

BST bst = new BST(new NameComparator());
Kingamere
  • 9,496
  • 23
  • 71
  • 110
  • 1
    You shouldn't be able to invoke the `compare` method on `this.comp`, because of its wildcard type. – Andy Turner Jun 10 '16 at 21:35
  • I'm gonna add more later, but that's not the point of this question. I really wanted to know how to take care of generic types of the constructor – Kingamere Jun 10 '16 at 21:35
  • @Andy Turner then how would I be able to pass in a comparator of different types and perform different compare operations? – Kingamere Jun 10 '16 at 21:36
  • From what you have here, yes; this is the right general approach. But there's not much specific help we can give without specific information. – James Tanner Jun 10 '16 at 21:36
  • 4
    You could make the class itself generic: `MyClass` and then only let the constructor accept comparators of the same type (`public MyClass(Comparator comp)`). – Mick Mnemonic Jun 10 '16 at 21:37
  • @MickMnemonic I want the constructor to accept only two types: `String` and another custom class I created. How do I do that? – Kingamere Jun 10 '16 at 21:41
  • 2
    @Kingamere you would need a type variable declared on the class (e.g. `MyClass`), which you use in the field and parameter type declarations (e.g. `Comparator` or, if you want to be a bit more generic, `Comparator super T>`). – Andy Turner Jun 10 '16 at 21:42
  • You'd need your "custom class" to implement an interface because Java doesn't have multiple inheritance. – Mick Mnemonic Jun 10 '16 at 21:46
  • I did what you said and now I'm getting the error: `The method compare(T, T) in the type Comparator is not applicable for the arguments (String, String)` – Kingamere Jun 10 '16 at 21:47
  • @Kingamere please provide that code, you're doing something wrong there. – Louis Wasserman Jun 10 '16 at 21:48
  • @LouisWasserman please check edit in question – Kingamere Jun 10 '16 at 21:53
  • You're creating the BST wrong; it should be `BST bst = new BST(new NameComparator());` – Louis Wasserman Jun 10 '16 at 21:54
  • @LouisWasserman ok that's a different error that I can fix easily, but I'm encountering the above error in the BST class itself where I do this: `...this.c.compare("c", "b") < 0...`. I'm just trying to compare Strings to see if it recognizes, and as Andy Turner said above "You shouldn't be able to invoke the compare method on this.comp, because of its wildcard type". So that's the problem – Kingamere Jun 10 '16 at 21:56
  • @Kingamere, how is a `Comparator` of something other than strings supposed to compare two strings? You've already stated that your `BST` should work on at least one class that isn't `String`, so how is that supposed to compare `"c"` and `"b"`? – Louis Wasserman Jun 10 '16 at 21:56
  • If you want to support you custom type also, you could do it like this (even though you get a compiler warning because `String` is `final`): `MyClass`, but then the comparator needs to be able to handle both strings and your custom class as Louis mentioned. – Mick Mnemonic Jun 10 '16 at 21:58
  • @LouisWasserman I want it compare my custom type including strings. I will try Mick's suggestion below – Kingamere Jun 10 '16 at 22:00
  • @MickMnemonic, that doesn't work even a little bit. That works for things that are _both_ `String` and `MyCustomInterface`, not one _or_ the other. @Kingamere, it sounds like you need _two_ comparators, one for strings and one for your other class. You can't have a `Comparator` that will compare both. – Louis Wasserman Jun 10 '16 at 22:00
  • @MickMnemonic you're saying your suggestion to do `MyClass` won't work? – Kingamere Jun 10 '16 at 22:01
  • @Louis, correct you are. The compiler warning is there _for an obvious reason_; such a `T` cannot exist. :) – Mick Mnemonic Jun 10 '16 at 22:01
  • @LouisWasserman You're not understanding my problem. I DO in fact have a comparator for both, what I want to do is have one constructor in a class, that can accept BOTH comparators. I want my binary search tree's constructor to accept both comparators: String type AND my custom class type – Kingamere Jun 10 '16 at 22:01
  • @Kingamere then you need `public class BST { public BST(Comparator stringComparator, Comparator myCustomClassComparator) { ... } }` – Louis Wasserman Jun 10 '16 at 22:06
  • @LouisWasserman Sorry I meant to say, I want the constructor to accept EITHER OR, not BOTH. – Kingamere Jun 10 '16 at 22:07
  • You could make those comparators implement a common marker interface, but anyways the design seems a bit flawed.. – Mick Mnemonic Jun 10 '16 at 22:07
  • Then you're out of luck, if your goal is a _constructor._ Your only alternative is to use factory methods: `public class BST { private BST(Comparator cmp) { ... } public static BST stringBST() { return new BST(stringComparator); } public static BST myTypeBST() { return new BST(myTypeComparator); } }` That is as good as you'll be able to get. (And a `BST` will _still_ not be able to write `comparator.compare("b", "c")`.) – Louis Wasserman Jun 10 '16 at 22:08
  • @LouisWasserman that's quite odd. Then how does Java's `TreeMap` class accept any type of comparator? Says in spec: "The map is sorted according to the natural ordering of its keys, or by a **Comparator provided at map creation time**..." I intent to replicate that. I thought it would be much easier. – Kingamere Jun 10 '16 at 22:13
  • @Kingamere you don't need to _restrict_ it to a `String` or your type? Then you're back to the class you already have that you've edited into the question, where you can choose what type goes into the BST and what type you need to be able to compare. But you can't guarantee that that type is `String` and you can't just call `this.c.compare("c", "b")` when this might _not_ be a BST for strings. – Louis Wasserman Jun 10 '16 at 22:15
  • 1
    *"Then how does Java's TreeMap class accept any type of comparator?"* - It doesn't. It accepts any comparator of a specific type (depending on ``). Not a comparator that could be one of two *different* specific types. Java does not support the kind of typing that you want. – Stephen C Jun 11 '16 at 00:04
  • @Stephen C makes sense now – Kingamere Jun 11 '16 at 00:23

1 Answers1

0

Your requirements seem a bit special and you cannot make use of generic types, because String is a final class, which your "special class" cannot extend.

You could perhaps make this work by using a marker interface, which both of your Comparator classes need to implement:

/** Marker interface for special comparators */
public interface SpecialComparator{}

public class NameComparator implements Comparator<String>, SpecialComparator{
    @Override
    public int compare(String s1, String s2) {
      return s2.compareTo(s1);
    }
}

public class BST {
    /* binary search tree */
    ...
    private SpecialComparator c;

    /* pass in comparator to constructor*/
    public BST(SpecialComparator c) {
      this.c = c;
    }
}
Mick Mnemonic
  • 7,808
  • 2
  • 26
  • 30
  • 1
    What is this actually accomplishing, then? – Louis Wasserman Jun 10 '16 at 22:18
  • @LouisWasserman, type safety, which is what marker interfaces accomplish, in general (see also Effective Java, Item 37). If you come up with a better suggestion given the constraints, feel free to share your answer. – Mick Mnemonic Jun 10 '16 at 22:21
  • Fine, would it be easier to have the constructor accept one Comparator of ANY type, instead of just restricting it to `String` and my "special type" ? – Kingamere Jun 10 '16 at 22:23
  • No, if your code is only designed to work with these two types of comparators. – Mick Mnemonic Jun 10 '16 at 22:25
  • @MickMnemonic, eh? Yes, it's easier _not_ to restrict which types you accept; then you don't have to resort to marker interfaces and the code the OP already has in the original question is correct. – Louis Wasserman Jun 10 '16 at 22:26
  • @LouisWasserman then please tell me, what do I pass into the `compare()` method when I invoke `c.compare`? Because trying `c.compare("s1", "s2")` is giving me that error. – Kingamere Jun 10 '16 at 22:28
  • @LouisWasserman what do I pass as the arguments to `compare`? – Kingamere Jun 10 '16 at 22:29
  • what are you _trying_ to compare? Why are you trying to compare them? You can write `BST bst = new BST(new NameComparator()); bst.c.compare("s1", "s2")`, if you like, but you should only be comparing things that are supposed to go into that kind of BST. You're writing `c.compare("s1", "s2")` when `c` might be a `Comparator`, and that makes no sense. – Louis Wasserman Jun 10 '16 at 22:30
  • The `compare` method is inside the BST class. For adding elements to the tree, I am comparing the value of the data on each node, to the data element being added, and then inserting the data element in the appropriate node. At this point, I think it might just be easier to have two different BST classes: One that accepts comparators of only `String` and the other for my "special class". – Kingamere Jun 10 '16 at 22:34
  • @Kingamere, is that BST for the "special class" going to have `compare("s1", "s2")` in it? That's the part that doesn't make sense. Comparing the value of the data on each note to the data element being added makes sense, and _both of those things_ should be of type `T`. It sounds like you haven't provided us enough of your code to help you resolve that issue. – Louis Wasserman Jun 10 '16 at 22:39
  • @LouisWasserman Ok I see what you are saying. I will read more on generics to clear things up and try to change those objects to type `T`. Thanks for the help. – Kingamere Jun 10 '16 at 22:42
  • A cleaner approach would be to define an `Either` class (there are a few patterns for doing that, none of them super elegant in Java ;) ) and use a `Comparator>` – yshavit Jun 10 '16 at 22:42
  • @yshavit, you're right (at least about the [Java solutions](http://stackoverflow.com/questions/9975836/how-can-i-simulate-haskells-either-a-b-in-java) not being very elegant) :) – Mick Mnemonic Jun 10 '16 at 22:49