Not sure if I am wording this correctly. Please let me know if you require more information.
We have a requirement where we need to determine the type of variable based on a system environment variable.
So, say we have a the following class
class Test
{
DUMMY_TYPE testVariable;
}
The DUMMY_TYPE is determined based on a system variable. So when Java compiles, is it possible to have Java use the System Environment variable to determine the type at compile type?
Also, is it possible to set this up somehow on Eclipse, where Eclipse will continue to show me DUMMY_TYPE on the IDE wherever I use it, but when compiling and building it can substitute DUMMY_TYPE with the correct type based on environment variable?
I have an idea.
Make the testVariable of type Object (or a DummyType class which extends Object). Then you can load the variable with whatever data you want using the primitive wrapper classes, based on what you read from your system variable.
So:
public class Test {
Object testVariable;
{
String whichType = null;
//Logic to read your system variable from wherever and store it in whichType
if(whichType.equals("int")) {
testVariable = new Integer(intVal);
}
else if(whichType.equals("double")) {
testVariable = new Double(doubleVal);
}
//etc.
}
Of course, this isn't Java "figuring out" which type it is at compile time like you want, necessarily (and the assignment would take place at run time, when the Test object was created), but it seems like reasonable framework for an alternative.
And you can also set the value of the testVariable upon initialization as appropriate, naturally.
Alternatively, you could have a method like this which accepts inputs as a String (read from your system variable) and returns it in the appropriate wrapper class of the primitive type:
public Object getPrimitiveValue(String type, String value) {
if(type.equals("int")) {
return new Integer(Integer.parseInt(value));
}
else if(type.equals("double")) {
return new Double(Double.parseDouble(value));
}
//etc.
}
Related
if we have MakeObject(String Name) and calling this method will create Object [Name] = new Object();
How can we do that? And, if we want to move the Object [Name] from the method to a public variable in class Clazz ?
Example:
public class clazz {
void MakeObject(String name) {
Object[name] = new Object();
[name].UpgradeToClassVariable(); // It should be a global variable
}
}
Usage of clazz :
public class main {
public static void main(String[] args) {
clazz c = new clazz();
c.makeObject("Hello");
c.Hello.doOperations(); // For example, if it's a String, doOperation() can be equals()
}
}
In java, . is the dereference operator: Take the thing to the left of the 'dot'. This must be a reference. If it is a reference to nothing (null), a NullPointerException occurs.
The thing to the right is the message you send to it.
Thus, sending an object the message 'make yourself global' doesn't make any sense. Something like MyClass.makeGlobal([name]) would, but java doesn't support this.
A class's structure is defined at compile time. You cannot, at runtime, add properties, or change a field from being instance to static.
The general idea of 'I want to add a property' still exists, but, not at the level you're talking about. Perhaps you want a Map<String, Integer> for example.
Take your second snippet:
clazz c = new clazz();
c.makeObject("Hello");
c.Hello.doOperations(); // For example, if it's a String, doOperation() can be equals()
That's just not how java works. Java will first compile code (javac), and then run it. javac does not run any code, it only compiles it. Thus, javac sees c.Hello and stops you right there, and says: Hello? What? I have no idea what you're talking about.
That's why it is not possible to use a string literal to make a field like this.
Java isn't javascript, or python, or ruby.
C# 6.0 introduced the nameof() operator, that returns a string representing the name of any class / function / method / local-variable / property identifier put inside it.
If I have a class like this:
class MyClass
{
public SomeOtherClass MyProperty { get; set; }
public void MyMethod()
{
var aLocalVariable = 12;
}
}
I can use the operator like this:
// with class name:
var s = nameof(MyClass); // s == "MyClass"
// with properties:
var s = nameof(MyClass.OneProperty); // s == "OneProperty"
// with methods:
var s = nameof(MyClass.MyMethod); // s == "MyMethod"
// with local variables:
var s = nameof(aLocalVariable); // s == "aLocalVariable".
This is useful since the correct string is checked at compile time. If I misspell the name of some property/method/variable, the compiler returns an error. Also, if I refactor, all the strings are automatically updated. See for example this documentation for real use cases.
Is there any equivalent of that operator in Java? Otherwise, how can I achieve the same result (or similar)?
It can be done using runtime byte code instrumentation, for instance using Byte Buddy library.
See this library: https://github.com/strangeway-org/nameof
The approach is described here: http://in.relation.to/2016/04/14/emulating-property-literals-with-java-8-method-references/
Usage example:
public class NameOfTest {
#Test
public void direct() {
assertEquals("name", $$(Person.class, Person::getName));
}
#Test
public void properties() {
assertEquals("summary", Person.$(Person::getSummary));
}
}
Sadly, there is nothing like this. I had been looking for this functionality a while back and the answer seemed to be that generally speaking, this stuff does not exist.
See Get name of a field
You could, of course, annotate your field with a "Named" annotation to essentially accomplish this goal for your own classes. There's a large variety of frameworks that depend upon similar concepts, actually. Even so, this isn't automatic.
You can't.
You can get a Method or Field using reflection, but you'd have to hardcode the method name as a String, which eliminates the whole purpose.
The concept of properties is not built into java like it is in C#. Getters and setters are just regular methods. You cannot even reference a method as easily as you do in your question. You could try around with reflection to get a handle to a getter method and then cut off the get to get the name of the "property" it resembles, but that's ugly and not the same.
As for local variables, it's not possible at all.
You can't.
If you compile with debug symbols then the .class file will contain a table of variable names (which is how debuggers map variables back to your source code), but there's no guarantee this will be there and it's not exposed in the runtime.
I was also annoyed that there is nothing comparable in Java, so I implemented it myself: https://github.com/mobiuscode-de/nameof
You can simply use it like this:
Name.of(MyClass.class, MyClass::getProperty)
which would just return the String
"property"
It's also on , so you can add it to your project like this:
<dependency>
<groupId>de.mobiuscode.nameof</groupId>
<artifactId>nameof</artifactId>
<version>1.0</version>
</dependency>
or for Gradle:
implementation 'de.mobiuscode.nameof:nameof:1.0'
I realize that it is quite similar to the library from strangeway, but I thought it might be better not to introduce the strange $/$$ notation and enhanced byte code engineering. My library just uses a proxy class on which the getter is called on to determine the name of the passed method. This allows to simply extract the property name.
I also created a blog post about the library with more details.
Lombok has an experimental feature #FieldNameConstants
After adding annotation you get inner type Fields with field names.
#FieldNameConstants
class MyClass {
String myProperty;
}
...
String s = MyClass.Fields.myProperty; // s == "myProperty"
My code:
FileChooser prompt = new FileChooser();
prompt.setTitle("Odaberi fajl");
source = (Source) prompt.showOpenDialog(new Stage());
where source is a class that extends File as such:
import java.io.File;
public class Source extends File {
public Source(String pathname) {
super(pathname);
}
}
returns an error when trying to cast to Source. I have no idea what is causing this.
FileChooser returns a File. What makes you think you can cast it to Source? It is not a Source.
What you want to do is one of:
Make Source encasulate a File and provide whatever custom methods you need, delegating to the contained File as appropriate.
public class Source {
private File f;
public Source(File f) {
this.f = f;
}
// Custom methods
...
// Delegating methods
public boolean exists() {
return f.exists();
}
...
}
Extend File as you are doing, but provide a constructor that takes another File (i.e. a copy constructor) and instantiates the Source using the data from the passed File.
public class Source extends File {
public Source(File f) {
super(f.getAbsolutePath());
}
// Custom methods
...
}
Then instantiate as follows:
FileChooser prompt = new FileChooser();
prompt.setTitle("Odaberi fajl");
source = new Source(prompt.showOpenDialog(new Stage()));
You need to understand what casting does.
Casting is a compile-time directive; it tells the compiler that the operation being performed (showOpenDialog() in your case) is going to return an object that matches the cast. If that turns out not to be true, then the program will throw an IllegalCastException at runtime. It is up to the programmer who writes the cast to ensure that the cast is going to be correct at runtime. The compiler will tell you if there is no possible way for it to be correct, in many cases, but cannot tell you in all cases.
FileChooser was written without knowledge of your Source class, so it is not possible for it to return a Source object. If the cast had succeeded, you would be allowed (by the compiler) to call methods from Source on the resulting object, and that would clearly be incorrect.
One thing to realize about casting is that, except in very limited circumstances involving boxed primitives, casting does not change the object referred to at all. Its purpose is to let the compiler know that operations will be legal on the resulting class.
As an aside, it helps enormously when asking a question to say what happens, not just 'returns an Error'. Is that a compile or runtime error? What does the error say? Please remember that for questions you ask in the future.
I have a cache that compartmentalizes by namespace. I would like to use class type to determine which cache to use. The following method gives an idea of what I want to accomplish. I use the word cache loosely. I am more interested in correcting my design pattern so it works.
public static DObject getFromCache(String key,Class<T extends DObject> type) {
MyCache cache = getWithName(type.getName());
// ......
type.class value = (type.class) cache.get(key);
// ......
}
where DObject is a naming interface. How do I fix so that type.class value = (type.class) cache.get(key) works well? I know this would be a joke for Scala but I want to use Java.
T value = type.cast(cache.get(key));
(You can declare variables of type T just fine, but type erasure means you can't cast to T. Luckily Class#cast is there to help.)
I am writing an application using java JDBC that queries and inserts data into an Oracle database.
I'm using the SimpleDriverDataSource from springframework API to implement the standard JDBC DataSource Inteface.
here is part of my code
dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(Class.forName(credentials.getDriverClass()));
I'm trying to keep the code independent of DriverClass used, and I know that class.forName() returns a class object for the class string name.
The problem is that I'm getting a compilation error saying:
the method setDriverClass(Class<? extends Driver>) in the type SimpleDriverDataSource is not applicable for the arguments (Class<capture#1-of ?>
I don't really understand what these symbols mean, or what is causing the error?
The SimpleDriverDataSource#setDriverClass(Class) is implemented as
public void setDriverClass(Class<? extends Driver> driverClass) {
this.driver = BeanUtils.instantiateClass(driverClass);
}
So it is expecting a Class object of a type that is a subtype of Driver.
The Class.forName(String) method is implemented as
public static Class<?> forName(String className)
throws ClassNotFoundException {
return forName0(className, true, ClassLoader.getCallerClassLoader());
}
In other words it returns a Class<?> object, ie. a Class object of any type, not necessarily one that is a subtype of Driver. Therefore the declared type of the returned object is not a valid argument to the setDriverClass() method.
One solution is to instantiate your Driver class yourself and use the setDriver(Driver) method instead
Class<?> clazz = Class.forName(credentials.getDriverClass());
Object driver = BeanUtils.instantiateClass(clazz);
dataSource.setDriver((Driver) driver);
Note that the above will throw a ClassCastException at runtime if the class you try to instantiate is not a subtype of Driver.
Alternatively, as suggested by BalusC you can cast the value returned by Class.forName()
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass((Class<Driver>)Class.forName("com.mysql.jdbc.Driver"));
Adding some #SuppressWarnings if you don't like IDE warnings.
This is a little trick for Java Generics that's worth knowing.
The problem happens when you are dealing with type parameters that you
know are consistent, but which are not explicit in your code. This is
common if you are processing a not-completely-typed collection.
To make things clearer, I'll use the following example: Consider a system
that transfers various values from one place to another (perhaps it is a
scheduler sending messages of different types within a system).
We might have an interface that can both provide and receive certain
message types:
public interface Connection<Type>
{
Type read();
void write(Type value);
}
And our scheduler might look something like this:
class Scheduler
{
public void process(Collection<Connection<?>> cnxs)
{
for (Connection<?> cns: cnxs) {
cnx.write(cnx.read);
}
}
}
(Note that is shorthand for and we use it here
because the cnxs collection contains a Connections with a variety of
different type parameters).
Unfortunately that won't compile! The error given be Eclipse with Java
1.6 is "The method write(capture#2-of ?) in the type
Connection is not applicable for the arguments
(capture#3-of ?)".
The reason this won't compile is that the type parameter for the value
being returned by the Connection and the type parameter for the value it
will receive are being treated separately. Each is being treated as
"capture-of ?" which means "some subclass of Object". And the compiler is
then (understandably) saying "I can't send 'subclass X of Object' to a
method that expects 'subclass Y of Object' because I don't know if they
are the same subclass".
To make this work we need to introduce the common type parameter
explicitly. Unfortunately the following code, or something like it,
doesn't work (as far as I can tell). There is no way to introduce a type
parameter in the middle of a block of code (what we really want here is
better support for polymorphism):
class Scheduler
{
public void process(Collection<Connection<?>> cnxs)
{
// syntax error!
for (<E> Connection<E> cns: cnxs) {
E value = cnx.read();
cnx.write(value);
}
}
}
But what we can do is add a helper method that introduces a new type
parameter:
class Scheduler
{
public void process(Collection<Connection<?>> cnxs)
{
for (Connection<?> cnx: cnxs) {
helper(cnx);
}
}
private <E> void helper(Connection<E> cnx)
{
E value = cnx.read();
cnx.write(value);
}
}
This does what we want! The code validates, compiles, and runs.
In summary then: Sometimes you can "lose" an explicit generic type
parameter (often because you are dealing with a collection of different
types). You can re-introduce that type parameter by adding an extra
helper method.