Philosophically, it may be argued that they're not different.
But they're completely different in why, where & how they're used and implemented.
First, both hard and soft/symbolic links are directory entries, ie they're entries/records in a specially formated file called a directory, where each record holds a name and a inode number -- the latter referring to the real structure holding the file's metadata (permissions, size, etc) and the list of data blocks -- structure which is called an "inode".
The difference between a hard and a symbolic link is that while the inode a hard link points to contains the actual data, the inode a symbolic link points to contains the path to another file. Basically, symlinks are themselves hard links which point to a special kind of file, just like named pipes, unix domain socket files, etc.
Now, when the kernel opens a file, it creates an in-memory structure duplicating the inode from the disk, and containing some of the same metadata (permissions, list of blocks containing data, size, etc). This structure is pointed to by another structure called an "open file description" (which holds things like the position pointer, whether the file was opened in read-write or read-only mode, whether it should be read and written in non-blocking mode, etc). Finally, this structure is pointed to by an entry in another kernel (per-process) structure, an array/table called the file descriptor table.
The "file descriptor", the integer returned by system calls like open(2), is an index into this table.
An in-memory inode can be pointed to by multiple "open file descriptions", which itself can be pointed to by multiple file descriptors, from multiple processes.
Some of the system calls like open(2) will treat symlinks specially -- when the file they're opening turns out to be a symlink, they go on trying to open the file named by the path which is its content.
Both the disk structures (directories, inodes) and the kernel/memory structures (inodes, open file descriptions) are using reference counting in order to keep track of which is using which. (No there isn't any mark & sweep garbage collector in the kernel which jumps through pointers in memory or on disk to determine which objects are no longer referred to from anywhere else ;-)).