Cast von Float nach String klappt nicht

huhu01

Mitglied
Hallo, bitte helft mir mal. Der formal type ist Object, der actual type ist Double. Wenn ich folgendes schreibe:

new Column().header("Rate").with(o1 -> ((Float) o1.get(1)).toString())

dann gibt es einen Laufzeitfehler: class java.lang.Double cannot be cast to class java.lang.Float (java.lang.Double and java.lang.Float are in module java.base of loader 'bootstrap')

Ich muss also:

new Column().header("Rate").with(o1 -> ((Float) ((Double) o1.get(1)).floatValue()).toString())

schreiben. Das empfinde ich eher als unschön.

Und ein String.format("%f", o1.get(1)) verhält sich leider nicht exakt so wie ein Float#toString...
 
Ich verstehe gerade nicht Dein Problem. o1.get(1) gibt dir ein Object und Du willst einfach nur toString() aufrufen. Die Methode gibt es ja bereits in Object, also warum nicht ein einfaches o1.get(1).toString()?
 
Du verstehst das Problem echt nicht? Ich will toString() auf Float aufrufen, und nicht auf Double? Damit nicht alle nervigen Nachkommastellen ausgegeben werden...
 
Java Float und Java Double leiten beide von Java Number ab und von nicht irgendwie von einander. Wieso sollte ein Cast da funktionieren?
 
Das Problem ist also wirklich das spezifische Format, das gewünscht wird. Das ist aus Deinem ersten Post so nicht hervor gegangen und das toString() ist halt direkt aufrufbar.

Das wäre dann also etwas wie: Float.toString(((Double)o1.get(1)).floatValue())

Generell ist das aber ggf. etwas, das ich auch etwas auslagern würde in eine Methode oder gar Klasse, denn das ist etwas relativ spezifisches. Ggf. gibt es in der Applikation noch mehr solcher Varianten.

Und bezüglich Clean Code würde ich da natürlich auch anmerken: Das Datenmodel / die Datenhaltung kann man ggf. prüfen, in wie weit man da nicht sogar noch etwas eingreifen kann, um eben mehr Typsicherheit zu bekommen (Und als Nebeneffekt den Code vereinfachen / lesbarer gestalten / weniger Fehleranfällig zu machen. Ist klar - get gibt ein Object. Wenn das also mal kein Double ist, dann knallt es).

Das einfach mal als ein paar Gedanken von meiner Seite dazu.
 
Die Methode gibt es ja bereits in Object, also warum nicht ein einfaches o1.get(1).toString()?
Die Implentierung von toString ist nicht ident, glaube ich. Beide gehen zwar nach FloatingDecimal.getBinaryToASCIIConverter fuer die Repraesentation, aber die Implementierung nach float und double sind unterschiedlich. Ich bin da bei Flieszkomma nicht ganz drinnen, aber kann gut sein dass die teilweise einen signifikanten Unterschied liefern.

Java Float und Java Double leiten beide von Java Number ab und von nicht irgendwie von einander. Wieso sollte ein Cast da funktionieren?
Du hast dich verlesen, die Wandlung auf Float wird auf dem floatValue() von dem Double aufgerufen.
 
Was Nachkommastellen angeht ... wieso nicht einfach
Java:
String.format("%.5f", doubleValue);
Damit kriegst du eben genau die gewünschten 5 Nachkommastellen

Edit: 0 bis 5 Nachkommastellen ist natürlich auch möglich über das Decimalformat.
 
Zuletzt bearbeitet:
Die Implentierung von toString ist nicht ident, glaube ich. Beide gehen zwar nach FloatingDecimal.getBinaryToASCIIConverter fuer die Repraesentation, aber die Implementierung nach float und double sind unterschiedlich. Ich bin da bei Flieszkomma nicht ganz drinnen, aber kann gut sein dass die teilweise einen signifikanten Unterschied liefern.
Ja, die Genauigkeit ist natürlich unterschiedlich. Das hat der TE ja dann auch als Beweggrund genannt. War für mich in so fern nur etwas befremdlich, als dass ich eben gewohnt bin, Formatierung dann eher ganz vorzugeben. Also wenn es um ein Format angeht, dann würde ich auch nicht sagen: "Das von Float gefällt mir besser als das von Double", sondern ich würde hin gehen und klar sagen: Die Anzeige an der Stelle ist genau so und so. Also z.B. eine Anzeige von bis zu 4 Nachkommastellen.

Und das würde sich dann vermutlich in einer eigenen Klasse befinden und dann wäre das ein Aufruf a.la. coolFormularFormater.formatRate(o1.get(1)) So es um die "Rate" in dem "CoolFormular" ginge 🙂

Und vielleicht unterstelle ich da auch schon wieder viel zu viel ... evtl. sind es eigentlich float Werte (weil das ausreicht) und irgend ein anderes System hat generell double. Dann wäre es natürlich kein Formatierungsproblem mehr und die Diskussion führt in die falsche Richtung.

Aber damit sind wir von dem Thema auf jeden Fall weiter weg. Irgend einen Tod muss man da sterben - wir müssen erst den Typ wiederherstellen (Cast zu Double) und den float Wert holen. Ob man da nun wieder ein Autoboxing macht oder nicht ist dann fast egal. Da kann jeder schauen, was er besser findet.
 
Was Nachkommastellen angeht ... wieso nicht einfach
Java:
String.format("%.5f", doubleValue);
Damit kriegst du eben genau die gewünschten 5 Nachkommastellen

Edit: 0 bis 5 Nachkommastellen ist natürlich auch möglich über das Decimalformat.
Weil ich eine variable Anzahl an Nachkommastellen beibehalten möchte, ggf. auch die scientific notation... Wie gesagt, String#format verhält sich anders als ein toString...

Ein Teil des Problems wurde auch hier beschrieben: https://stackoverflow.com/questions/5980958/java-impossible-cast-object-to-float-why .
 
Weil ich eine variable Anzahl an Nachkommastellen beibehalten möchte, ggf. auch die scientific notation... Wie gesagt, String#format verhält sich anders als ein toString...
Eine variable Anzahl an Nachkommastellen kriegst du mit dem DecimalFormat hin.
format.setMinimumFractionDigits(0);
format.setMaximumFractionDigits(5);

Wenn du das öfters brauchst, schreib dir eine Methode, die genau das macht, dann am besten auch gleich deine bevorzugte Locale verwendet.

Was float.toString() intern macht, weiß ich nicht. Dass es zufällig deinen Anforderungen entspricht ist toll, aber ich würde mich nicht darauf verlassen. Am besten immer selbst bestimmen, wie die Ausgabe auszusehen hat.
 
Hier ist einmal das gesamte Program. Ich nutze folgende Library für die Tabelle-Darstellung: https://github.com/freva/ascii-table

Java:
package org.example;

import com.github.freva.asciitable.AsciiTable;
import com.github.freva.asciitable.Column;
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

public class Main {
    private static ArrayList<Swap> orders;
    private static Date date;
    private static ArrayList<ArrayList<Comparable>> rows;

    public static JSONArray getJsonArray_a() throws IOException {
        URL url = new URL("...");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.addRequestProperty("content-type", "application/json");
        connection.addRequestProperty("x-api-key", "...");
        connection.setUseCaches(false);
        connection.setDoInput(true);
        connection.setDoOutput(true);
        PrintWriter writer = new PrintWriter(connection.getOutputStream());
        writer.println("{\"currency\":\"ALGO\",\"codes\":[\"ALGO\",\"OPUL\",...],\"sort\":\"rank\",\"order\":\"ascending\",\"offset\":0,\"limit\":0,\"meta\":false}");
        writer.close();
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        StringBuilder builder = new StringBuilder();
        String line = "";
        while ((line = reader.readLine()) != null) {
            builder.append(line).append("\n");
        }
        System.out.println("builder = " + builder);
        return new JSONArray(builder.toString());
    }

    static class Swap {
        public String code_a;
        public String code_b;
        public double sum_a;
        public double sum_b;
        public double rate;

        public Swap(String code_a, String code_b, double sum_a, double sum_b) {
            this.code_a = code_a;
            this.code_b = code_b;
            this.sum_a = sum_a;
            this.sum_b = sum_b;
            rate = sum_a / sum_b;
        }
    }

    public static double getDbl(JSONObject o, String k) {
        if (!o.has(k) || o.isNull(k)) {
            return -1;
        }
        return o.getDouble(k);
    }

    public static void update() throws IOException {
        orders = new ArrayList<>();
        if (new File("my_algo_view.txt").canRead()) {
            BufferedReader reader = new BufferedReader(new FileReader("my_algo_view.txt"));
            String l;
            while ((l = reader.readLine()) != null) {
                String[] sa = l.split(";");
                orders.add(new Swap(sa[0], sa[1], Double.parseDouble(sa[2]), Double.parseDouble(sa[3])));
            }
            reader.close();
        }

        date = new Date();
        JSONArray a = getJsonArray_a();
        rows = new ArrayList<>();
        for (int i = 0; i < a.length(); i++) {
            JSONObject o1 = a.getJSONObject(i);
            JSONObject o2 = o1.getJSONObject("delta");
            ArrayList<Comparable> r = new ArrayList<>();
            r.add(o1.getString("code"));
            r.add(o1.getDouble("rate"));
            Swap s = null;
            for (Swap swap : orders) {
                if (swap.code_b.equalsIgnoreCase(o1.getString("code"))) {
                    s = swap;
                }
            }
            if (s != null) {
                r.add((float) (o1.getDouble("rate") / s.rate * 100.0));
            } else {
                r.add(-1f);
            }
            r.add(getDbl(o2, "hour"));
            r.add(getDbl(o2, "day"));
            r.add(getDbl(o2, "week"));
            r.add(getDbl(o2, "month"));
            r.add(getDbl(o2, "quarter"));
            r.add(getDbl(o2, "year"));
            rows.add(r);
        }
    }

    public static void show() {
        System.out.println(date);
        System.out.println(AsciiTable.getTable(rows, Arrays.asList(
                new Column().header("Code").with(o1 -> o1.get(0).toString()),
                new Column().header("Rate").with(o1 -> ((Float) ((Double) o1.get(1)).floatValue()).toString()),
                new Column().header("%").with(o1 -> o1.get(2).toString()),
                new Column().header("h").with(o1 -> o1.get(3).toString()),
                new Column().header("d").with(o1 -> o1.get(4).toString()),
                new Column().header("w").with(o1 -> o1.get(5).toString()),
                new Column().header("mon").with(o1 -> o1.get(6).toString()),
                new Column().header("qua").with(o1 -> o1.get(7).toString()),
                new Column().header("year").with(o1 -> o1.get(8).toString())
        )));
    }

    public static void main(String[] args) throws IOException {
        while (true) {
            System.out.println("Options: 1:show 2:update 3:sort_by 4:add_swap");
            int o = new Scanner(System.in).nextInt();
            if (o == 1) {
                if (rows == null) {
                    update();
                }
                show();
            } else if (o == 2) {
                update();
                show();
            } else if (o == 3) {
                int c = new Scanner(System.in).nextInt() - 1;
                rows.sort(Comparator.comparing(o1 -> o1.get(c)));
                show();
            } else if (o == 4) {
                System.out.println("String code_a, String code_b, double sum_a, double sum_b");
                String[] sa = new Scanner(System.in).nextLine().split(" ");
                orders.add(new Swap(sa[0], sa[1], Double.parseDouble(sa[2]), Double.parseDouble(sa[3])));

                PrintWriter writer = new PrintWriter("my_algo_view.txt");
                for (Swap s : orders) {
                    writer.println(s.code_a + ";" + s.code_b + ";" + s.sum_a + ";" + s.sum_b);
                }
                writer.close();

                show();
            } else {
                break;
            }
        }
    }
}

Wie man sieht, ist in der Methode "show" nur bei der "Rate"-Spalte das Problem mit dem casting. 🙁

Meine IDE schlägt mir vor, ArrayList<Comparable> als Datentyp zu vermeiden ... Bin für Verbesserungen offen. 🙂
 
Also das Format der Antwort ist ja klar - da würde ich einmal einen Datentyp erstellen um dann mit Jackson oder gson das JSON direkt umzuwandeln. Damit hat man die im Thread zuerst angesprochenen Probleme dann so nicht mehr.

Meine IDE schlägt mir vor, ArrayList<Comparable> als Datentyp zu vermeiden ...
Ja, eine wirkliche Typisierung wäre sinnvoller. Dann hättest Du eine Datenklasse und dann halt eine List<Datenklasse>.
Bei der nicht vorhandenen Typisierung hast Du beim Sortieren den Vorteil, dass Du einfach nach einer id sortieren kannst.

Die Warnung der IDE ist aber hier zu vernachlässigen - wenn Dich so Clean Code Themen interessieren, dann sähe ich da noch einige andere Punkte, die aus meiner Sicht viel dringender wären 🙂
 

Zurück
Oben