#Runtime types vs Generics
1 messages · Page 1 of 1 (latest)
<@&987246399047479336> please have a look, thanks.
Here is an AI assisted attempt to answer your question 🤖. Maybe it helps! In any case, a human is on the way 👍. To continue talking to the AI, you can use </chatgpt:1108714622413963314>.
then you would typically use runtime types instead of generics. Generics are more commonly used when the type information is known at compile time.
Generics allow you to write reusable code that can work with different types without sacrificing type safety. They provide compile-time type checking and help in writing more flexible and maintainable code.
On the other hand, if you are dealing with situations where the type information is not known until runtime, such as deserializing JSON data or working with reflection, then using runtime types may be more appropriate.
Overall, the choice between using generics or runtime types depends on the specific requirements of your project and how the type information is determined. Keep in mind that using generics can help in catching errors at compile time, while using runtime types may provide more flexibility in certain scenarios.
It's important to understand the advantages and limitations of both approaches to make an informed decision based on your project's needs. Experiment with both options and see which one works best for your particular situation.
There are two simple ways to return something that is of a type that you input.
-
Generics. You declare the generic when declaring the method, and then you add it as a param, do something, and return it back. T represents anything that is passed and is only available during compile time.
-
Objects. All classes extend the "Object" class. This is why you can cast an object to anything and if you know that the type casted is valid it will work.
This makes generics more "useful". They do not require things to be casted (for the most part) because you don't have to cast something that is already that type. Generics also give you the option to go even more extreme and make your code even better.
You should use an Object when processing data where the types are scattered around and you can't expect the same result. (An example would be calling a method from Class A to Class B that accepts an Object varargs. Then, you return those varargs back to another method in Class A to process the varargs Object. You could not use generics for this as the varargs could be of different types and sizes.
Yeah, it sounds like Object is going to be my only option here because the return types are determined at runtime rather than compile time, hence my struggle with generics.
it depends on the situation. you could simply use abstraction
You could use a wrapper class that uses generics inside of it to store whatever so when you get it its already done for you.
generics exist for compile-time safety, and thats it
I'm creating a tool that reads in a schema from yaml and then generates data for it. I often won't know the data type defined at runtime. It's similar to a database in the fact that it will often implicitly cast values to the correct type.
I would make a class like this, but I just realized this wouldn't work
you would need to check and then cast it if you know the data types you're working with
Yeah, I had to build a bunch of utility methods to convert primitives around and then I have classes that deal with getting an object input and just use runtime type inspection to determine how to cast it. So if I get a String type and I want to store it in an integer, the integer class handles how to convert that string into a number, like how a database knows that if you insert '3' into an int column, it becomes 3
I was just wondering if there was a better way but honestly, I think I'm just learning that every language that casts values has to go through this same process.
Yep.
Although, you should look into if maybe calling a util method that converts the type instead of auto-converting?
I just feel that might help in the future, because you are explicitly doing it in the code instead of it doing it in the background if that makes sense. It might help with debugging later
¯_(ツ)_/¯
up to you
just my preference
Could you clarify what you mean?
Like
putting a string in an integer value in a db or whatever
you would have to convert it like so: Integer.parseInt or Integer.valueOf, instead of placing it in and letting your code that places it inside convert it for you*
and therefor you know exactly what you are doing and can solve any issues that arise fast without having to go on a hunt
its a preference for sure, but just something to also think about and be weary of.
The way I have it right now is that each data type is in charge of casting their own values. I have a data type for each of the data types in the RDBMS. So a PostgresIntDataType is in charge of how to convert an object into an Integer type, which often uses Integer.parseInt or Double.parseDouble().intValue();
Just because there are some database types that are backed by a certain type, yet are more restrictive or simply have different restrictions than java types
Such as MySQL's Mediumint type. It's backed by a 3 byte int but it was significantly easier to just use Java's 4 byte int and then check if it's out of bounds
This is similar to what I was explaining above, and also works. It's different from the other setup I was talking about as you're essentially connecting the methods/casting instructions to the actual data type which makes it easy to debug and control.
I don't think you can really go farther into improving it unless I'm missing something.
Good to know. Just ugly as sin code to look at
You could use generics and just do Thing<?> almost everywhere
so for those few cases you do actually know you can do something with it
Its the casting
I did do a lot of that, when I could
idk if postgres has a "sealed" type system but you could maybe make use of that?
like "a pgtype is one of uuid, text, ..."
but if not yeah - welcome to Object
lol
at the very least you can make a marker interfacae
interface PgType {}
so you have a place to start when figuring out "what can this return"
but if you have a truly dynamic thing, you have a truly dynamic thing
thems the ropes
I also have a crazy idea
I kind of did that. I created a class for each of the database types and then each one has a method to take in an object and then return a casted type. So, PostgresIntegerDataType implements DataType<Integer> so it returns an integer from an object
you could have a web and use that to convert something into a "middle man" data type so that you can convert that aswell. This will cut down on the amount of logic you need to implement.
However it would probably be messy and really hard to figure out how to use if you forgot it.
I'm just throwing stuff out here
so like a UUID type of all ints
you have a string
so string --> int --> UUID
but again it has many drawbacks
maybe you could find a way to use some of my wack advice in your code
anyways good luck
Thanks for the help. Good to at least know I'm not crazy
ofc
i think you might also be a little biased by the way java is "shaped"
if you were making an equivalent thing in clojure you wouldn't care nearly as much
since dispatch isn't based on types
but all the same mechanics are available in the end
I think it would be a little easier in C# too just because they have the dynamic keyword so I could defer the type to runtime instead of having to do all the casting behavior in Java since there is no dynamic keyword
Is it not supported just because Java has static dispatch instead of dynamic dispatch?
hmm
not sure tbh
although...
1 sec
i starred a "dynamic" library some time ago
I think I was reading that C# does static dispatch except for the dynamic types, which is dynamic dispatch but I could be totally confusing terms
you are
but its gonna be exhausting to drill into it
i can't find the lib so give me like 5 min to make an example
static void Main(string[] args)
{
ExampleClass ec = new ExampleClass();
// The following call to exampleMethod1 causes a compiler error
// if exampleMethod1 has only one parameter. Uncomment the line
// to see the error.
//ec.exampleMethod1(10, 4);
dynamic dynamic_ec = new ExampleClass();
// The following line is not identified as an error by the
// compiler, but it causes a run-time exception.
dynamic_ec.exampleMethod1(10, 4);
// The following calls also do not cause compiler errors, whether
// appropriate methods exist or not.
dynamic_ec.someMethod("some argument", 7, null);
dynamic_ec.nonexistentMethod();
}
c# for reference
Dynamic is basically duck typing in C# right?
yeah thats not something you could solve with generics, since it occurs at runtime
public final class Dynamic {
private final Object value;
private Dynamic(Object o) {
this.value = o;
}
public static Dynamic of(Object o) {
if (o instanceof Dynamic d) {
return d;
}
else {
return new Dynamic(o);
}
}
public Dynamic method(String methodName, Object... args) throws Exception {
for (var method : value.getClass().getDeclaredMethods()) {
if (method.getName().equals(methodName)) {
try {
return Dynamic.of(method.invoke(value, args));
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
throw new RuntimeException("No such method");
}
public Object unwrap() {
return value;
}
}
public class Main {
public static void main(String[] args) {
var example = Dynamic.of(new ExampleClass());
example.method("exampleMethod1", 10, 4);
}
}
obviously you'd want maybe a smarter way of picking a method or field or whatever
and i wouldn't reccomend building this just to avoid a cast
but dynamic doesn't exactly require a language feature
at least from what i can tell
its just a way to make it more ergonomic to do what you could otherwise do with reflection
dynamic languages on the jvm have been able to do it for ages
(let [o (ExampleClass.)]
(.exampleMethod1 o 10 4))
but even there you try to avoid it because it neccesarily implies a performance cost
(let [o (ExampleClass.)]
(.ExampleClass/exampleMethod1 o 10 4))
Yeah, I'm aware that I'll have some performance cost but thus far, it hasn't been too poor performance wise. The whole point of my project anyway is to seed databases and so it's not really to keep the data within the application or build something around it. It's basically, come in with the schema and leave with generated data to build something else on the data.
Something where the type is more defined and more dependable. Since I don't know what types will be provided by the user at runtime, I have all this ugly casting stuff but hopefully I can still keep it mostly maintainable.