Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent method call using compiler tricks

I have roughly these types:

interface Record {}
interface UpdatableRecord extends Record {}

interface Insert<R extends Record> {

    // Calling this method only makes sense if <R extends UpdatableRecord>
    void onDuplicateKeyUpdate();
}

I would like to have an additional restriction on <R> when calling onDuplicateKeyUpdate(). The reason for this is that this method only makes sense, when <R> is bound to any subtype of UpdatableRecord, not just Record. Examples:

Insert<?>               i1;
Insert<Record>          i2;
Insert<UpdatableRecord> i3;

// these shouldn't compile
i1.onDuplicateKeyUpdate();
i2.onDuplicateKeyUpdate();

// this should compile
i3.onDuplicateKeyUpdate();

Is there any trick or way that I can use to add an additional restriction for the class' generic type just for one method declaration?

NOTE:

  • Declaring Insert<R extends UpdatableRecord> is not an option, because I want the flexibility of having an Insert<R extends Record> for records that are not updatable
  • Deriving UpdatableInsert<R extends UpdatableRecord> extends Insert<R> and pushing the method down there might be an option, but I don't really want to introduce a new type. I have several of these methods with several different restrictions, which would lead to a type explosion.
  • Throwing UnsupportedOperationException is quite obvious, but it seems the Java 1.4 way of doing it. I really wonder if I can use generics and the compiler for this issue.

Ideal solution:

// This would be an annotation to be interpreted by the compiler. There is no such
// thing in Java, as far as I know. But maybe there's a trick having the same effect?
@Require(R extends UpdatableRecord)
void onDuplicateKeyUpdate();
like image 482
Lukas Eder Avatar asked Jul 17 '26 03:07

Lukas Eder


1 Answers

If you're defining it in the interface, then it must be implemented in any class that wishes to implement the interface. So there is no way you can get around implementing onDuplicateKeyUpdate in any class that implements the Insert<R extends Record> interface.

You've specified that R extends Record in the interface and so this becomes part of the contract, which means that any subtype of Record can be used. I can't think of a way to add an additional constraint using generics that restricts it to only of type UpdatableRecord.

Also, you cannot make decisions in your interface based on the type of R because that information goes away due to type erasure.

It seems to me that your problem revolves around the difference between Record and UpdatableRecord. Since an interface is meant to be general contract, I don't think that type-specific behavior should be in your interface. So you would have to figure out a way to resolve the difference another way. My solution would be to implement a method (that returns boolean) in the Record interface called canUpdate that you can use in onDuplicateKeyUpdate. This way you can throw an UnsupportedOperationException if the record doesn't support that operation or do nothing (if you want to avoid the exception, and if doing nothing makes sense in the context of your business logic).

This is off the top of my head; there may be a better solution. I'll try to think about it some more.

EDIT

I thought about this a little more and it was your latest edit that made me think about this. The <R extends Record> applies to the entire class. So there's no way to override or narrow it down in the context of a specific method. An annotation like the one you mentioned needs to be implemented at the compiler level and at that level I'm not even sure if you will have access to the type of R (because of type erasure). So what you're saying may not be possible.

On a conceptual level, what you're asking for shouldn't be possible IMO because you're specifying an exception to the contract within your contract. It also sounds like implementation details are creeping into your interface because the interfaces shouldn't try to figure out what something can or cannot do; that should be decided in the concrete implementation.

like image 127
Vivin Paliath Avatar answered Jul 19 '26 18:07

Vivin Paliath



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!