0

Here is a class I am working on:

public class Thing<T extends Comparable<? super T>> {
   private Map<String, List<SourcedValue<T>>> properties;
}

Then, SourcedValue is this:

public class SourcedValue<T extends Comparable<? super T>> 
                         implements Comparable<****?*****> {
  private T value;
  private List<Sources> sources;

  @Override
  public int compareTo(SourcedValue<****?****> other) {
    return value.compareTo(other);
  }
}

What do I put into the ***?***?

What I need to do is sort the List<SourcedValue<T>> in the Thing in some convert method that creates Thing and populates its properties, (along with its List<SourcedValue<T>> for each property).

user1902183
  • 3,203
  • 9
  • 31
  • 48
  • 1
    Feels like a bridge too far to me. This would be code that obscures more than it helps. Hard to write, hard to read, not a good abstraction. That Map feels like maybe it should be another object. I'd recommend a simpler approach. – duffymo Jun 07 '19 at 17:19
  • It's property map of property name to list of possible values. Each value has to have a source. The actual value can be a String, Date, custom object, whatever it is, but Comparable so values for a particular property can be sorted. I don't see how how else you'd represent that situation. – user1902183 Jun 07 '19 at 17:24
  • Granted, this isn't a `Hello World` situation, but that is actually fairly easy to deal with... What complicated all this is the `Comparable` requirement. – user1902183 Jun 07 '19 at 17:26
  • That's just a multi-map. If you abstracted type and value into a single abstraction you could manage this. – duffymo Jun 07 '19 at 17:26
  • 1
    I think it's hard b/c you keep value and type separate. I find that when it'd difficult to express in code it might be a sign that I'm not thinking about the problem properly. – duffymo Jun 07 '19 at 17:27
  • @duffymo What do you mean keep value and type separate. Can you expand on that? Show alternative? – user1902183 Jun 07 '19 at 17:30

2 Answers2

0

I am not sure if this will help you, but this is one way I see to implement your requirement. I will be glad if this helps you.

public class SourcedValue<T extends Comparable<? super T>>
    implements Comparable<SourcedValue<? super T>> {
private T value;
private List<Integer> sources;

@Override
public int compareTo(SourcedValue<? super T> o) {
    return value.compareTo((T)o.value);
    }
}

Also use of super appears to be redundant here. Even the below solution should produce the same result

public class SourcedValue<T extends Comparable<T>>
    implements Comparable<SourcedValue<T>> {
private T value;
private List<Integer> sources;

@Override
public int compareTo(SourcedValue<T> o) {
    return value.compareTo(o.value);
}
}
Anand Vaidya
  • 1,374
  • 11
  • 26
  • If you remove `return 0` and actually place code in there like `return value.compareTo(o);`, you'll see that this doesn't compile. – user1902183 Jun 07 '19 at 17:34
  • Probably type-casting might help.. but I don't have setup to try running it. You might want to try. – Anand Vaidya Jun 07 '19 at 17:37
  • I doubt it. What would you cast it to?? It's *already* declared as `SourcedValued` – user1902183 Jun 07 '19 at 17:39
  • Please check the updated answer. For some reason, It does not understand the captor, but eventually its T to be compared with another T – Anand Vaidya Jun 07 '19 at 17:41
  • Oh, I see. Thanks! That does work. Unfortunately, it feels a bit off to me. I am curious what my conversation with @duffymo will produce. Perhaps, I am approaching this whole thing wrong. – user1902183 Jun 07 '19 at 17:49
  • BTW whats the role of `super` in this situation? I find it redundant. – Anand Vaidya Jun 07 '19 at 18:00
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/194613/discussion-between-anand-vaidya-and-user1902183). – Anand Vaidya Jun 07 '19 at 18:03
  • This whole structure was initiated by this question: https://stackoverflow.com/questions/56486386/how-to-declare-java-list-of-object-where-each-one-implements-comparable – user1902183 Jun 07 '19 at 18:03
  • 1
    The first approach is wrong, as the necessity to insert an *unchecked* type cast demonstrates. The second approach is too restrictive, e.g. you can not create a `SourcedValue` with that. The correct declaration is `public class SourcedValue> implements Comparable>`. – Holger Aug 21 '19 at 07:48
  • Ok. Let me check that. – Anand Vaidya Aug 21 '19 at 07:52
0

Here's how I might do it:

TypedProperty.java

/**
 * A TypedProperty class - more than String.
 * @link https://stackoverflow.com/questions/56498727/how-to-declare-java-comparable-in-this-situation
 */
public class TypedProperty<T extends Comparable<T>> implements Comparable<TypedProperty<T>> {

    private final T value;
    private final Class<T> type;

    public TypedProperty(T value, Class<T> clazz) {
        this.value = value;
        this.type = clazz;
    }

    public T getValue() {
        return value;
    }

    public Class<T> getType() {
        return type;
    }

    @Override
    public int compareTo(TypedProperty<T> other) {
        return this.value.compareTo(other.value);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        TypedProperty<?> that = (TypedProperty<?>) o;

        if (!getValue().equals(that.getValue())) return false;
        return getType().equals(that.getType());
    }

    @Override
    public int hashCode() {
        int result = getValue().hashCode();
        result = 31 * result + getType().hashCode();
        return result;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("TypedProperty{");
        sb.append("value=").append(value);
        sb.append(", type=").append(type);
        sb.append('}');
        return sb.toString();
    }
}

A JUnit test to prove that it works:

import org.junit.Assert;
import org.junit.Test;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * @link https://stackoverflow.com/questions/56498727/how-to-declare-java-comparable-in-this-situation
 */
public class TypedPropertyTest {

    @Test
    public void testConstructor_DateTypedProperty() throws ParseException {
        // setup
        String [] testDates = { "2019-06-07", "2018-03-17", "2017-01-01" };
        List<String> expected = Arrays.asList(testDates);
        Collections.sort(expected);
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        List<TypedProperty<Date>> typedProperties = new ArrayList<>();
        for (String testDate : testDates) {
            typedProperties.add(new TypedProperty<>(format.parse(testDate), Date.class));
        }
        // test
        Collections.sort(typedProperties);
        // assert
        for (int i = 0; i < typedProperties.size(); ++i) {
            Assert.assertEquals(format.parse(expected.get(i)), typedProperties.get(i).getValue());
        }
    }
}
duffymo
  • 305,152
  • 44
  • 369
  • 561
  • So, how does `Thing` declare it then? Specifically for its `properties` property? – user1902183 Jun 07 '19 at 19:21
  • I think I see where you want to go, but I'm not sure how to get you there. You want to be able to have a Property class that's more strongly typed than the one the JDK gives you, right? I don't know how you plan to mingle a mix of these into a single data structure. Have to think about it a bit more. – duffymo Jun 07 '19 at 19:25
  • Right... In your unit test, you are using concrete types. However, in my utility methods, I have to use the generic type because I don't know what type it will actually be. I just need to know that it's `Comparable`. The `properties` will be used to pull out values from `List>`. There will be times when I need to populate that list as well (like when I get a bunch of sourced values). The original structure I showed was proposed as the answer to my 1st question: https://stackoverflow.com/questions/56486386/how-to-declare-java-list-of-object-where-each-one-implements-comparable – user1902183 Jun 07 '19 at 19:39
  • As an aside `SimpleDateFormat` and `Date` are poorly designed and long outdated, the former in particular notoriously troublesome. Instead I recommend you use `LocalDate` from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Jun 08 '19 at 12:03
  • 1
    Agreed. I did it quickly. The new classes are better. Example only. – duffymo Jun 08 '19 at 12:34