Problem mit Class.forName()

Hi!

Erst mal vorweg. Ich poste nun etwas größere Code-Teile. Das liegt aber daran, daß ich nicht wüßte, wie ich es NOCH WEITER kürzen könnte.

Erst einmal die Programmdateien:

Module1.java (der Einstiegspunkt ins Programm):


public class Module1 {

   public static Form1 myForm1 = new Form1();

   public static void main(String[] args) {
      myForm1.setSize(500, 400);
      myForm1.setLocation(100, 100);
      myForm1.setTitle("Form");
      myForm1.setVisible(true);
      myForm1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }

}```

Form1.java:
```import javax.swing.*;
import VB.Form;

public class Form1 extends Form {

   public void Form_Load() {
      try {
        JOptionPane.showMessageDialog( null, Class.forName("VB.Form").toString() );
      } catch (Exception e) {
        JOptionPane.showMessageDialog( null,  e);
      }
   }

   public Form1() {
      this.fireLoaded();
   }
}```

VB/Form.java:
```package VB;

import VB.ILoadAdapter;
import VB.LoadAdapter;
import javax.swing.JFrame;

public class Form extends JFrame implements ILoadAdapter {

   public void fireLoaded() {
      LoadAdapter tmpFireAdapter = new LoadAdapter((Object)this, (ILoadAdapter)this);
      tmpFireAdapter.fireLoaded();
   }

   public void fireLoaded(Object sender) {
      this.setLocation(50, 50);
      this.setSize(200, 200);
      this.setVisible(true);
      this.Form_Load();
   }

   public void Form_Load() {
   }

   public Form() {
      this.initVars();
   }

   private void initVars() {}
}```

LoadAdapter.java:
```package VB;

import VB.ILoadAdapter;
import javax.swing.*;

public class LoadAdapter implements Runnable {

   private ILoadAdapter myContainer;
   private Object mySender;


   public LoadAdapter(Object refSender, ILoadAdapter refContainer) {
      super();
      this.mySender = refSender;
      this.myContainer = refContainer;
   }

   public void fireLoaded() {
      try {
        SwingUtilities.invokeAndWait((Runnable)this);
      } catch (Exception e) {
        JOptionPane.showMessageDialog( null,  e);
      }
   }

   public void run() {
      this.myContainer.fireLoaded(this.mySender);
   }
}```

ILoadAdapter.java:
```package VB;

public interface ILoadAdapter {

   void fireLoaded();

   void fireLoaded(Object var1);
}```

Der Punkt ist nun folgender:
In der Klasse **Form1** wird das "**Class.forName("VB.Form")**" erfolgreich ausgegeben. Wenn ich hingegen die Hauptklasse einsetze "**Class.forName("Module1")**" hängt das Programm mittendrin und gibt die Klasse NICHT im JOptionPane aus.
Bei JEDER ANDEREN Klasse funktioniert es OHNE PROBLEME. Nur die Hauptklasse will er nicht.
Wenn ich in **Module1** die Initalisierung von **myForm1** in die main()-Methode schreibe, dann funktioniert es auch wunderbar.
In den meisten Fällen gibt es auch keine Probleme damit. Aber in diesem einen, für mich wichtigen Fall, schon. Warum ist das so?
Warum kann ich Module1 nicht ausgeben?
Was kann ich an den Dateien unter VB (VB/Form, VB/LoadAdapter, VB/ILoadAdapter) ändern, damit es auch mit Module1 funktioniert?

Grüße
theuserbl

Die Ursache ist, dass die Variable myForm1 static ist. Damit gehört das Initialisieren dieser Variablen mit new Form1() zum Intitialisierungsprozess der Klasse Module1. Im Konstruktor von Form1() würdest Du wiederrum versuchen genau diese Klasse zu laden, die ja nur vollständig geladen werden kann, wenn der Konstruktor von Form1 vollständig abgearbeitet ist…

Lösche einfach das static weg und greife über die Instanz von Module1 auf sie zu anstatt als Klassenvariable.

P.S. Wenn es nicht nur eine Übung ist, erschließt sich mir der Sinn des Codes nicht. Da es alles Deine Klassen sind, kannst Du doch auch direkt mit den Klassenobjekten arbeiten. Warum der Umweg über Class.forName(…)?

[QUOTE=nillehammer]Die Ursache ist, dass die Variable myForm1 static ist. Damit gehört das Initialisieren dieser Variablen mit new Form1() zum Intitialisierungsprozess der Klasse Module1. Im Konstruktor von Form1() würdest Du wiederrum versuchen genau diese Klasse zu laden, die ja nur vollständig geladen werden kann, wenn der Konstruktor von Form1 vollständig abgearbeitet ist…[/QUOTE]Soweit ist das richtig. Ein simples “Class.forName()” versucht die Klasse zu initialisieren und im Falle von “Modul1” tut es dies aus dem bereits laufenden Initialisierungsprozess der Klasse “Modul1” heraus. Wäre dieser Initialisierungsprozess nicht threadsicher, würde es eine Endlosrekursion ergeben, so aber hast du 'ne Race-Condition.
Wenn du “myForm” aber statisch lassen willst, solltest du “Class.forName()” anders aufrufen, dann klappts auch mit “Module1”.


import javax.swing.*;
import VB.Form;

public class Form1 extends Form {

	public void Form_Load() {
		try {
			Class<?> clazz = Class.forName("Module1", false, ClassLoader.getSystemClassLoader());
			JOptionPane.showMessageDialog(null, clazz.toString());
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null, e);
		}
	}

	public Form1() {
		this.fireLoaded();
	}
}```
BTW.: Das ist hoffentlich nur 'ne Übung... ;)

@Spacerat : Danke für den Hinweis auf die überladenen Varianten von forName. Hatte ich nicht bedacht!

[QUOTE=Spacerat]Wenn du „myForm“ aber statisch lassen willst, solltest du „Class.forName()“ anders aufrufen, dann klappts auch mit „Module1“.[/QUOTE]Oh, danke. mit Deiner modifizierten Version funktioniert es nun. :slight_smile:
Hatte mich bisher auch nicht soweit damit auseinandergesetzt, um zu sehen, daß Class.forName() auch in einer Variante mit mehr als einem Attribut existiert. Ich wäre auch niemals darauf gekommen, es so lösen zu können. Danke noch mal.

BTW.: Das ist hoffentlich nur 'ne Übung… :wink:
Wie meinen? Das Jabaco Framework ist so aufgebaut. Mit Deiner Hilfe kann ich dort nun einen Bug beseitigen.

Grüße
theuserbl

[QUOTE=theuserbl]Wie meinen? Das Jabaco Framework ist so aufgebaut. Mit Deiner Hilfe kann ich dort nun einen Bug beseitigen.[/QUOTE]Ich meinte, dass das nur 'ne Übung sein kann bzw. sein sollte, zumindest was die Klassen “Modul1” und “Form1” angeht.

  1. Die Sache mit dem statischen JFrame (Form1)
  2. Den JFrame noch nicht mal auf dem EDT konstruiert
  3. Der MessageDialog bei jeder neuen Form
  4. “setVisible()” in einem Konstruktor
  5. Das sind alles NoGos und nur zu Übungszwecken oder KSKBs geeignet. Wenn man sucht, findet man gewiss noch andere Dinge.
  1. Ein Package-Bezeichner in Großbuchstaben

[QUOTE=Landei]6. Ein Package-Bezeichner in Großbuchstaben[/QUOTE]Das hat nichts zu sagen. Form_Load() ist auch eine Methode die mit einem Großbuchstaben anfängt. Das hat damit zu tun, daß Jabaco ein VisualBasic6-Dialekt für die JavaVM ist. Und wie bei BASIC-Dialekten üblich, ist die Sprache case-insensitive. Bzw. Methoden werden dort geradezu mit einem großen Buchstaben begonnen.

Hier eine Klasse des Jabaco-Frameworks die in Java geschreiben ist (die meisten Klassen sind in Jabaco selber geschrieben):
http://code.google.com/p/jabacoframework/source/browse/trunk/Framework/src/VBA/Interaction.java
Insbesondere statische Methoden beginnen mit einem Großbuchstaben. Der Grund: Öffentliche statische Methoden, lassen sich von jeder Klasse aus direkt aufrufen.
Statt wie in Java


public class Module1
{
  public static void main(String[] args) {
    Interaction.MsgBox("Hallo Welt!");
  }
}```
schreibt man in Jabaco bloß
```Public Sub main(ByJava args() As String)
   MsgBox "Hallo Welt!"
End Sub```

Aber die Vorteile spielt Jabaco gerade im GUI-Bereich aus. Kein actionPerformed() und so.

Wenn man im GUI-Designer einen Button zeichnet, wird eine Instanz namens **Command1** von der Klasse **CommandButton** (die ihrerseits übrigens von **JButton** abgeleitet ist) erstellt. Und welcher Code wird ausgeführt, wenn man auf diesen Button klickt? Alles was in der Methode **Command1_Click()** steht. Hier z.B. ein Fenster das sich öffnet, wenn man auf den Button klickt:
```Public Sub Command1_Click()
   MsgBox "Button wurde gedrückt"
End Sub```

Grüße
theuserbl

[QUOTE=Landei]6. Ein Package-Bezeichner in Großbuchstaben[/QUOTE]Wenn man „sucht“ usw… Sag’ ich doch - öhm. :wink:

[QUOTE=theuserbl;63999]Aber die Vorteile spielt Jabaco gerade im GUI-Bereich aus. Kein actionPerformed() und so.

Wenn man im GUI-Designer einen Button zeichnet, wird eine Instanz namens Command1 von der Klasse CommandButton (die ihrerseits übrigens von JButton abgeleitet ist) erstellt. Und welcher Code wird ausgeführt, wenn man auf diesen Button klickt? Alles was in der Methode Command1_Click() steht. Hier z.B. ein Fenster das sich öffnet, wenn man auf den Button klickt:

   MsgBox "Button wurde gedrückt"
End Sub```

Grüße
theuserbl[/QUOTE]:lol: Jetzt wirds religiös... Ich hab zwar von VB kein Plan, deshalb die Frage: Bekommt jeder Button seine eigene Sub? Da lob' ich mir doch eher die ActionListener. Obwohl... Buttons sind unheimlich schlechte Beispiele, weil da hat jeder nunmal 'ne Eigene Funktion, es sei denn, man hat 'ne Klasse "Numeric Button"
```import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayDeque;

import javax.swing.JButton;

class NumericButton extends JButton {
	private static final long serialVersionUID = 1L;

	public NumericButton(char character) {
		super(String.valueOf(character));
	}
}

class NumericActionListener implements ActionListener {
	private Number number = new Number();
	private ArrayDeque<Number> register;
	private ArrayDeque<Character> operators;

	@Override
	public void actionPerformed(ActionEvent e) {
		JButton jb = (JButton) e.getSource();
		char c = jb.getActionCommand().charAt(0);
		switch(c) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			number.addDigit(c - '0');
			break;
		case '+':
		case '-':
		case '*':
		case '/':
			register.push(number.clone());
			number.reset();
			operators.push(c);
			break;
		case '.':
			number.setPoint();
			break;
		case '=':
			double v1 = register.pollFirst().getValue();
			double v2;
			while(!register.isEmpty()) {
				v2 = register.pollFirst().getValue();
				c = operators.pollFirst();
				switch(c) {
				case '+':
					v1 += v2;
					break;
				case '-':
					v1 -= v2;
					break;
				case '*':
					v1 *= v2;
					break;
				case '/':
					v1 /= v2;
					break;
				}
			}
			// ergebnis.setText(String.valueOf(v1));
		}
	}
}

class Number implements Cloneable {
	double value;
	int digits;
	boolean pointSet, markReset;

	public void addDigit(double i) {
		if(markReset) {
			pointSet = false;
			digits = 0;
			value = 0.0;
			markReset = false;
		}
		if(!pointSet) {
			value *= 10;
			value += i;
			return;
		}
		i /= ((++digits) * 10.0);
		value += i;
	}

	public void setPoint() {
		pointSet = true;
	}

	public void reset() {
		markReset = true;
	}

	public double getValue() {
		return value;
	}

	@Override
	public Number clone() {
		try {
			return (Number) super.clone();
		} catch(CloneNotSupportedException e) {
			// should never happen
			throw new RuntimeException(e);
		}
	}
}```Da ließe sich mit nur einem Listener ein ganzer Minitaschenrechner realisieren. Da merkt man den gravierenden Unterschied zu OOP.

[QUOTE=Spacerat]Jetzt wirds religiös… Ich hab zwar von VB kein Plan, deshalb die Frage: Bekommt jeder Button seine eigene Sub?[/QUOTE]Ja. Wenn man drei Buttons hat, die alle ihren Default-Namen behalten, gibt es Command1_Click(), Command2_Click() und Command3_Click(). Und entsprechend auch weitere Events dafür wie Button1_GotFocus(), Button1_LostFocus(), Button1_MouseEntered(), Button1_MouseExited(), … und noch einige mehr. In der GUI sind sie alle übersichtlich zu sehen.

Hier mal ein kleines Beispiel. Bei Form1 wurden im Designer zwei Buttons und ein Slider erstellt. Als Code für Form1 kann man dann folgendes schreiben:

   Slider1.Value = 5
End Sub

Public Sub Command2_Click()
   MsgBox(Slider1.Value)
End Sub

Public Sub Slider1_Change()
   Command2.Width = Slider1.Value * 5 + 2006
End Sub```

Drückt man also den ersten Button, dann verschiebt sich der Slider auf den Wert 5.
Drückt man auf den zweiten Button, wird in einem Info-Fenster der Wert ausgegeben, auf den der Slider gerade eingestellt ist.
Und wenn man den Slider-Regler verschiebt, dann ändert sich die Größe des zweiten Buttons im Verhältnis zum Wert des Sliders.

Einfacher kann man soetwas kaum schreiben.

Und ja, **Value** und **Width** sind Properties in Jabaco, ähnlich wie den entsprechenden Properties in VisualBasic 6, C# oder VisualBasic.net.


Und wie wird dieses Programm realisiert. Wie würde es in Java aussehen?

Jabaco erzeugt Java-Binary-Code, kein Java-Sourcecode. Somit kann man das nur beantworten, wenn man die Binaries dekompiliert.

Indem man die Buttons und den Slider erstellt, werden für Form1 zwei zusätzliche Klassen erstellt:

Form1$CommandButton:
```import VB.CommandButton;

public class Form1$CommandButton extends CommandButton {

   int ID;
   Form1 Owner;
   int Index;


   public void setOwner(Form1 var1, int var2) {
      this.Owner = var1;
      this.ID = var2;
      this.Index = -1;
   }

   public void _Click() {
      if(1 == this.ID) {
         this.Owner.Command1_Click();
      } else if(2 == this.ID) {
         this.Owner.Command2_Click();
      }
   }
}```

Form1$Slider:```import VB.Slider;

public class Form1$Slider extends Slider {

   int ID;
   Form1 Owner;
   int Index;


   public void setOwner(Form1 var1, int var2) {
      this.Owner = var1;
      this.ID = var2;
      this.Index = -1;
   }

   public void _Change() {
      if(3 == this.ID) {
         this.Owner.Slider1_Change();
      }
   }
}```

Und Form1 enthält zum einen, alles vom Designer erstellte und zum anderen diesen Code:```import VB.*;
import VBA.*;

public class Form1 extends Form {

   private Form1$CommandButton Command1;
   private Form1$CommandButton Command2;
   private Form1$Slider Slider1;


   public void Command1_Click() {
      this.Slider1.$Value(5);
   }

   public void Command2_Click() {
      Interaction.MsgBox(String.valueOf(this.Slider1.$Value()));
   }

   public void Slider1_Change() {
      this.Command2.$Width((float)(this.Slider1.$Value() * 5 + 2006));
   }

   public Form1() {
      this.initVars();
   }

   private void initVars() {
      this.$Transparency(0);
      this.$AntiAliasing(false);
      this.$AutoRedraw(false);
      this.$Picture((IResource)null);
      this.$DrawWidth(1);
      this.$BackColor((long)-2147483633);
      this.$ForeColor((long)-2147483630);

// sämtlicher Designer-Teil ...


      this.Command1 = new Form1$CommandButton();
      this.Command1.setOwner(this, 1);
      this.Command1.$Caption("Command1");
 // ...
      this.add((Component)Me.Command1);
    

  // ...
      this.Command2 = new Form1$CommandButton();
      this.Command2.setOwner(this, 2);  

  // ...

      this.add((Component)this.Command2);
 // ...
      this.Slider1 = new Form1$Slider();
      this.Slider1.setOwner(this, 3);

// ..
      this.add((Component)Me.Slider1);

 // ..


  }
}```

Soviel erst einmal dazu, um ein Gefühl zu bekommen, was Jabaco ist und wie es sich in Bezug zu Java verhält.

Grüße
theuserbl

Also, wenn ich einen Slider und 2 Buttons haben will, die den Slider bewegen muss ich 3 neue Klassen erstellen? Und das soll einfacher sein? Ich hab keinen Plan von VB aber afaik ist es eine bekannte Anfängersprache mit der man ohne viel Erfahrung kleine GUIs zusammenzimmern kann. Alleine bei deinem mini Beispiel sieht man aber schon wie komplex das ganze werden kann, wenn man fixe klassenübergreifende Referenzen auf Membervariablen hat. Das verstößt einfach gegen alle OO Prinzipien und je größer dein Programm wird umso unübersichtlicher.

[QUOTE=theuserbl]Ja. Wenn man drei Buttons … usw[/QUOTE]Du weist schon, dass es in Java 1.0 mit den Events ungefähr genauso war und warum man es geändert hat? Das entspricht keiner OOP-Konvention. Java ist aber OOP und wer das nicht kapiert, kann ja weiter VB programmieren. Aber warum sollte man in Java solche VB-Konstrukte verwenden wollen, während man auf der anderen Seite versucht, sie zu vermeiden?
In Java hat man diverse Listener-Interfaces, die man direkt an Componenten (Buttons usw) hängen kann oder in die EventQueue bzw. dem EventMulticaster einreihen kann. So kann man z.B. einen Listener für 100 Componenten (siehe mein Minitaschenrechner) oder 100 Listener für eine Komponente schreiben. Egal wofür man sich da entscheidet, sicher ist, dass die Listener-Methoden definitiv parallel (bei SingleCores natürlich pseudoparallel) zum eigentlichen Programm ausgeführt werden und diesen damit in keinster Weise stören (es sei denn, man programmiert Deadlocks o.ä.).