Title: Bug in generics architecture: FileHandle
Open Graph Title: Bug in generics architecture: FileHandle
X Title: Bug in generics architecture: FileHandle
Description: This issue is connected with #468 Below is a very simple test, using SCIFIO TiffParser: public class FileHandleGenericsBug { public static void main(String[] args) { if (args.length < 1) { System.out.println("Usage:"); System.out.println...
Open Graph Description: This issue is connected with #468 Below is a very simple test, using SCIFIO TiffParser: public class FileHandleGenericsBug { public static void main(String[] args) { if (args.length < 1) { System.o...
X Description: This issue is connected with #468 Below is a very simple test, using SCIFIO TiffParser: public class FileHandleGenericsBug { public static void main(String[] args) { if (args.length < 1) { Syste...
Opengraph URL: https://github.com/scijava/scijava-common/issues/469
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Bug in generics architecture: FileHandle\u003cFileLocation\u003e is not DataHandle\u003cLocation\u003e","articleBody":"This issue is connected with https://github.com/scijava/scijava-common/issues/468\r\n\r\nBelow is a very simple test, using SCIFIO TiffParser:\r\n\r\n```\r\npublic class FileHandleGenericsBug {\r\n public static void main(String[] args) {\r\n if (args.length \u003c 1) {\r\n System.out.println(\"Usage:\");\r\n System.out.println(\" \" + FileHandleGenericsBug.class.getName() + \" some_tiff_file\");\r\n return;\r\n }\r\n final File file = new File(args[0]);\r\n\r\n Context context = new SCIFIO().getContext();\r\n TiffParser parser = new TiffParser(context, new FileLocation(file));\r\n DataHandle\u003cLocation\u003e stream = parser.getStream();\r\n System.out.println(\"Successfully opened: \" + stream.getLocation());\r\n\r\n BytesLocation bytesLocation = new BytesLocation(1000);\r\n stream.set(bytesLocation); // - crash!! java.lang.ClassCastException\r\n\r\n System.out.println(\"This operator will not be performed\");\r\n context.close();\r\n }\r\n}\r\n\r\n```\r\n\r\nTiffParser input stream, as well as some analogous streams in other classes, is declared as `DataHandle\u003cLocation\u003e`. But really it is not a sub-type of `DataHandle\u003cLocation\u003e`! In terms of Java generics, it is `DataHandle\u003c? extends Location\u003e`\r\n\r\nUnfortunately, DataHandle is implemented as a container, that can store an object of the generic type, i.e. Location, alike in classes List\u003cLocation\u003e, Set\u003cLocation\u003e etc. In other words, it has a method \r\n`void set(Location loc);`\r\nIt means that we can pass to this method **any** Location subclass, for example, BytesLocation. As a result, a simple call \"stream.set(bytesLocation)\" in the code above throws ClassCastException.\r\n\r\nOf course, the compiler tries to warn about the problem, but you suppress the warning inside the method PluginInfo.loadClass:\r\n\r\n```\r\n\tpublic Class\u003c? extends PT\u003e loadClass() throws InstantiableException {\r\n\t\tif (pluginClass == null) {\r\n\t\t\ttry {\r\n\t\t\t\tfinal Class\u003c?\u003e c = Types.load(className, classLoader, false);\r\n\t\t\t\t@SuppressWarnings(\"unchecked\")\r\n\t\t\t\tfinal Class\u003c? extends PT\u003e typedClass = (Class\u003c? extends PT\u003e) c;\r\n\t\t\t\tpluginClass = typedClass;\r\n\t\t\t}\r\n\t\t\tcatch (final IllegalArgumentException exc) {\r\n\t\t\t\tthrow new InstantiableException(\"Class not found: \" + className, exc);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn pluginClass;\r\n\t}\r\n```\r\nFileHandle class \"supports\" FileLocation, so, it is created without exceptions, though actually FileHandle **is not**` DataHandle\u003cLocation\u003e`\r\n\r\nOf course, the same problem appears when I try to create `DataHandle\u003cLocation\u003e` with help of my own function:\r\n```\r\n /**\r\n * Warning: you should never call {@link DataHandle#set(Object)} method of the returned result!\r\n */\r\n @SuppressWarnings(\"rawtypes, unchecked\")\r\n public static DataHandle\u003cLocation\u003e getFileHandle(FileLocation fileLocation) {\r\n Objects.requireNonNull(fileLocation, \"Null fileLocation\");\r\n FileHandle fileHandle = new FileHandle();\r\n fileHandle.set(fileLocation);\r\n return (DataHandle) fileHandle;\r\n }\r\n```\r\n\r\nThe problem is essentially serious. Unfortunately, TiffParser, like many other classes based on DataHandle technique, declare the streams as `DataHandle\u003cLocation\u003e`, and it is a public declaration (for example, result of TiffParser.getStream() method is used in TIFFFormat class). I'm afraid you cannot just to replace `DataHandle\u003cLocation\u003e` with correct and safe `DataHandle\u003c? extends Location\u003e` - a lot of client will stop be compiled. \r\n\r\nBut, maybe, you can rework the class FileHandle (and BytesHandle, and other known implementations of DataHandle) to reduce the problem? I've tried to do this with FileHandle - see the attached file. Now it extends `AbstractDataHandle\u003cLocation\u003e,` not `AbstractDataHandle\u003cFileLocation\u003e`. I've added \"main\" test method to illustrate an idea: now \"set\" method works with Location instead of FileLocation, but it checks the type of the argument itself. However, I don't know how to correctly change the method getType() - it seems it will become a problem...\r\n\r\nIn any case, please think over the problem. Maybe you will find better solution.\r\n\r\n[FileHandle.zip](https://github.com/scijava/scijava-common/files/12278970/FileHandle.zip)\r\n\r\n","author":{"url":"https://github.com/Daniel-Alievsky","@type":"Person","name":"Daniel-Alievsky"},"datePublished":"2023-08-07T11:14:25.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/469/scijava-common/issues/469"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:8006f284-123f-4f62-0705-96b32a89bd3a |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | AC86:1F16D5:4E65DA:68EC7B:6969C5E7 |
| html-safe-nonce | 97a972fd71238a09c823bf6da61045e0337367f4883abc62942b164192000bae |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBQzg2OjFGMTZENTo0RTY1REE6NjhFQzdCOjY5NjlDNUU3IiwidmlzaXRvcl9pZCI6IjMyNjA0NzgxMDc1OTk2MTk1NTkiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 968d9f5173869018114887b7f60685f00f7d404f2951034875a1dd9f06ff44f4 |
| hovercard-subject-tag | issue:1839231628 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/scijava/scijava-common/469/issue_layout |
| twitter:image | https://opengraph.githubassets.com/50c175b0328b11e88c6ffc1489814c24374ca8f15635d96cca11f4685b77261f/scijava/scijava-common/issues/469 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/50c175b0328b11e88c6ffc1489814c24374ca8f15635d96cca11f4685b77261f/scijava/scijava-common/issues/469 |
| og:image:alt | This issue is connected with #468 Below is a very simple test, using SCIFIO TiffParser: public class FileHandleGenericsBug { public static void main(String[] args) { if (args.length < 1) { System.o... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | Daniel-Alievsky |
| hostname | github.com |
| expected-hostname | github.com |
| None | acedec8b5f975d9e3d494ddd8f949b0b8a0de59d393901e26f73df9dcba80056 |
| turbo-cache-control | no-preview |
| go-import | github.com/scijava/scijava-common git https://github.com/scijava/scijava-common.git |
| octolytics-dimension-user_id | 1262770 |
| octolytics-dimension-user_login | scijava |
| octolytics-dimension-repository_id | 3594497 |
| octolytics-dimension-repository_nwo | scijava/scijava-common |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 3594497 |
| octolytics-dimension-repository_network_root_nwo | scijava/scijava-common |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 83c08c21cdda978090dc44364b71aa5bc6dcea79 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width