What is a DataObject?
DataObjects wrap FileObjects. (If you do not want to visualize files on disk in an explorer view or create a text editor [with syntax coloring, etc] for files, then you will never need to touch DataObjects.) A FileObject is just a container for data; it happens to have a MIME type, but like java.io.File, it doesn't know or care what kind of file it represents, or what data it contains. DataObject is part of the Loaders API - a good overview of this API can be found here.
A DataObject represents one or more (typically only one) FileObjects. A DataObject knows what kind of a file it represents. It may represent the parsed contents of a file such as a .java file. Or,
as in the case of InstanceDataObject, the file name may have semantic meaning. For example, a file with the name
org-netbeans-modules-speech-SpeakAction.instance literally is an instruction to "Load the class
org.netbeans.modules.speech.SpeakAction, and create an instance of it using its default (no argument) constructor" (this technique is commonly used in the system filesystem to register Java objects installed by modules - more about that here).
DataObjects are produced by DataLoaders, which modules register for specific file types. For each file type, there is (usually) one DataLoader. For each file of that type, there is one DataObject.
DataObjects are seldom referred to by Java subclass. If you are casting a DataObject to its implementation class, you are probably doing something wrong. This is a general rule for which there can be exceptions, but is especially true if you're doing the cast from code in a different module than the one in which the DataObject was defined. Instead, the usage pattern is to ask a DataObject for instances of interfaces that are the things your code will actually interact with, by calling
DataObject.getLookup().lookup(SomeType.class) (more about Lookup).
As a simple example, the NetBeans APIs define an interface org.netbeans.api.actions.Openable. It has one method, open(). That method will open the file in the editor.
What exactly will happen when open() is called is entirely up to the module that implements the DataObject and Openable. The rest of the system does not need to know any of the implementation details - it just needs to know if the DataObject has an Openable. If it does, then the Open action on its context menu can be enabled, and that action will call theDataObject.getLookup().lookup(Openable.class).open().
As suggested above, a DataObject may actually represent more than one file - so when you expand a folder in the UI, there may actually be fewer DataObjects in that folder than there are files. This is why, in the NetBeans IDE, a Swing form is represented by a .java file and a corresponding .form file, but the .form file is not visible in the UI. .properties files have also used this mechanism to present multiple resource bundles in diverse languages as a single node in the files tree.
However, this ability to represent multiple files with a single DataObject is strongly discouraged for new code and will probably eventually be deprecated - it has serious negative implications for scalability.